How a tenant is designed, secured, and provisioned โ isolated by default, least-privilege, policy-enforced, and provisioned in one command with a namespace-scoped credential.
A new namespace denies all ingress and runs restricted Pod Security before any workload exists.
The tenant gets a Role scoped to their namespace โ never cluster scope, never their own guardrails.
No single control is the only protection โ RBAC and network policy and quota and Pod Security and admission policy.
Guardrails are Kyverno policies in Git, enforced at admission โ not tribal knowledge.
Every tenant's desired state lives in tenants/<name>/; Argo CD reconciles the cluster to it.
Onboarding is templated and idempotent; offboarding prunes everything and revokes credentials.
caas.tenant label: limits, non-root, no-privileged, approved registry.Request to mutate the cluster as a tenant โ โผ AuthN who are you? (SA token / OIDC group) โผ AuthZ RBAC: only tenant-admin Role in tenant-<name> โผ Admission Pod Security (restricted) rejects unsafe pods โผ Admission Kyverno: limits ยท non-root ยท no-privileged ยท registry โผ Quota ResourceQuota / LimitRange cap consumption โผ Runtime NetworkPolicy default-deny isolates traffic Any single layer failing still leaves the others.
# Platform team โ provision a tenant and issue its credential ./scripts/onboard-tenant.sh x [--mesh] [--allow-root] [--gitops-only]
The tenant is rendered from tenants/_template/ into tenants/x/ (token substitution + opt-in mesh/root labels).
All controls are applied to the cluster โ namespace first, then quota, limits, RBAC, ServiceAccount, default-deny network, and the AppProject.
A bound, time-limited token is minted from the tenant's ServiceAccount and packaged as out/tenant-x.kubeconfig โ scoped to the namespace, gitignored.
Give the kubeconfig to the tenant; commit tenants/x/ so Argo CD adopts and reconciles it as the source of truth.
# the issued kubeconfig is namespace-scoped โ verified: KUBECONFIG=out/tenant-x.kubeconfig kubectl get pods # works (their namespace) KUBECONFIG=out/tenant-x.kubeconfig kubectl get nodes # Forbidden KUBECONFIG=out/tenant-x.kubeconfig kubectl get pods -n kube-system # Forbidden