docs(gateway): rewrite security model to cover all four VAPs plus the render-time check
Address review feedback from kitsunoff on packages/extra/gateway/README.md:17: the security model section described only two layers, but packages/system/cozystack-basics/templates/gateway-hostname-policy.yaml ships four ValidatingAdmissionPolicies plus a render-time fail in cozystack-basics. Rewrite the section to cover each VAP's scope and what it guards against, plus the listener allowedRoutes whitelist and the render-time fail — six layers, each one explained. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
This commit is contained in:
parent
ccde31cc8b
commit
afa69148cb
1 changed files with 9 additions and 3 deletions
|
|
@ -14,10 +14,16 @@ Per-tenant Gateway API Gateway backed by Cilium. Installed automatically when `t
|
|||
|
||||
## Security model
|
||||
|
||||
Two layers protect cross-tenant isolation:
|
||||
Six independent layers protect cross-tenant isolation. Compromising one of them does not bypass the others; admission-time checks (layers 2–5) fail closed (`failurePolicy: Fail`, `validationActions: [Deny]`).
|
||||
|
||||
1. **Namespace whitelist on listeners.** The Gateway's `allowedRoutes.namespaces.from: Selector` only accepts HTTPRoutes from the publishing tenant's namespace and from `publishing.gateway.attachedNamespaces` in the platform chart (default: cozy-cert-manager, cozy-dashboard, cozy-keycloak, cozy-system, cozy-harbor, cozy-bucket, cozy-kubevirt, cozy-kubevirt-cdi, cozy-monitoring, cozy-linstor-gui). A tenant namespace that is not on this list cannot attach an HTTPRoute to this Gateway at all.
|
||||
2. **Hostname ownership via ValidatingAdmissionPolicy.** `cozystack-gateway-hostname-policy` (installed by cozystack-basics when `gateway.enabled` is true) rejects any Gateway whose listener hostnames fall outside the tenant's own domain suffix. For `tenant-root` the allowed suffix is `publishing.host`; for any `tenant-<name>` it is `<name>.publishing.host`. Other namespaces are rejected outright.
|
||||
1. **Namespace whitelist on listeners.** The Gateway's `allowedRoutes.namespaces.from: Selector` matches the built-in `kubernetes.io/metadata.name` label (written by kube-apiserver, unspoofable). It accepts routes from the publishing tenant's namespace plus `publishing.gateway.attachedNamespaces` in the platform chart (default includes the `cozy-*` namespaces for platform services and `default` for the Kubernetes API TLSRoute). A namespace outside the list literally cannot attach any `HTTPRoute` or `TLSRoute` to this Gateway.
|
||||
2. **`cozystack-gateway-hostname-policy`** — `ValidatingAdmissionPolicy` on `gateway.networking.k8s.io/v1 Gateway` CREATE/UPDATE. Reads `namespaceObject.metadata.labels["namespace.cozystack.io/host"]` and rejects any listener hostname that is not equal to that value or a subdomain of it. `matchConditions` gate the VAP to cozystack-managed namespaces only — Gateways in unrelated namespaces (e.g. `kube-system`) are not touched.
|
||||
3. **`cozystack-gateway-attached-namespaces-policy`** — VAP on `cozystack.io/v1alpha1 Package` CREATE/UPDATE. Rejects any `tenant-*` entry in `spec.components.platform.values.gateway.attachedNamespaces`. Catches direct `kubectl edit packages.cozystack.io` that would bypass the helm render-time guard in layer 6.
|
||||
4. **`cozystack-tenant-host-policy`** — VAP on `apps.cozystack.io/v1alpha1 Tenant` CREATE/UPDATE. Rejects setting or changing `spec.host` unless the caller's groups contain `system:masters`, `system:serviceaccounts:cozy-system`, `system:serviceaccounts:cozy-cert-manager`, `system:serviceaccounts:flux-system` or `system:serviceaccounts:kube-system`. Closes the path where a tenant user sets `spec.host=dashboard.example.org` on their own tenant to have the tenant chart write a hijacked label into the namespace.
|
||||
5. **`cozystack-namespace-host-label-policy`** — VAP on core `v1 Namespace` UPDATE. Rejects any change to the `namespace.cozystack.io/host` label once it is set, except by the same trusted-caller whitelist as layer 4. CREATE is unrestricted because the initial label write is part of the cozystack chart apply.
|
||||
6. **Render-time `fail` in cozystack-basics.** The cozystack-basics chart fails the helm render if `_cluster.gateway-attached-namespaces` contains any `tenant-*` entry. Triggers on the helm-install path before the cluster ever sees the values — complements layer 3 which triggers at `kubectl apply` time.
|
||||
|
||||
For `tenant-root` the allowed host suffix is `publishing.host`; for any `tenant-<name>` that inherits from its parent the suffix is `<name>.<parent apex>`. A child tenant with an independent apex (`customer1.io` instead of a subdomain) is handled correctly because the VAP reads the per-namespace label rather than assuming a subdomain hierarchy.
|
||||
|
||||
## Rate limits
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue