Platform Team ยท Design & Onboarding

Tenant onboarding

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.

Design

Principles

๐Ÿ›ก๏ธ Isolation by default

A new namespace denies all ingress and runs restricted Pod Security before any workload exists.

๐Ÿ”‘ Least privilege

The tenant gets a Role scoped to their namespace โ€” never cluster scope, never their own guardrails.

๐Ÿงฑ Defense in depth

No single control is the only protection โ€” RBAC and network policy and quota and Pod Security and admission policy.

๐Ÿ“ฆ Policy as code

Guardrails are Kyverno policies in Git, enforced at admission โ€” not tribal knowledge.

๐Ÿ” GitOps source of truth

Every tenant's desired state lives in tenants/<name>/; Argo CD reconciles the cluster to it.

โ™ป๏ธ Reproducible & revocable

Onboarding is templated and idempotent; offboarding prunes everything and revokes credentials.

Design

What every tenant gets โ€” security by construction

๐Ÿงญ
Namespace + Pod Security restrictedThe boundary. Blocks privileged/root/host-namespace pods at the namespace gate.
๐Ÿ“Š
ResourceQuota + LimitRangeCaps CPU/memory/pods/PVCs; zero LoadBalancers/NodePorts (ingress-only exposure).
๐Ÿ”
RBAC Role + ServiceAccount namespacedFull control of workloads in the namespace โ€” but not its own quota, limits, or network.
๐Ÿšง
NetworkPolicy default-denyDrops cross-tenant traffic; allows only ingress-nginx and Prometheus.
๐ŸŽฏ
Argo CD AppProjectScopes the tenant's GitOps apps to their namespace, approved repo, and approved kinds.
โš–๏ธ
Kyverno policies admissionAuto-applied via the caas.tenant label: limits, non-root, no-privileged, approved registry.
Design

Defense in depth

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.
How

Onboarding steps โ€” one command

# Platform team โ€” provision a tenant and issue its credential
./scripts/onboard-tenant.sh x [--mesh] [--allow-root] [--gitops-only]

Render

The tenant is rendered from tenants/_template/ into tenants/x/ (token substitution + opt-in mesh/root labels).

Provision

All controls are applied to the cluster โ€” namespace first, then quota, limits, RBAC, ServiceAccount, default-deny network, and the AppProject.

Issue credential

A bound, time-limited token is minted from the tenant's ServiceAccount and packaged as out/tenant-x.kubeconfig โ€” scoped to the namespace, gitignored.

Hand over & track

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
Application onboarding โ†’ Full design blueprint โ† Portal