2. Inside a cluster

2.6 Application multi-tenancy inside a zone

Requirements: R10, R13, R14, R22.

Namespace tenancy

Each application gets its own namespace(s) inside a zone, with a baseline policy set applied automatically: the picture shows default deny, DNS to CoreDNS, traffic within the namespace and inbound only from the zone’s gateway pods (label gateway.networking.k8s.io/gateway-name: <zone> in <zone>-gateway). Two applications of a zone talk to each other only through their published names on the zone’s gateway, HTTPS on 443, each call allowed by a rule in the caller’s rule file (section 5.1); nothing listens to another namespace directly, and inside its own namespace an application uses any protocol (R22). Beyond the picture:

  • in the stateful cluster, inbound comes only from the node subnets of the same zone in the stateless clusters (Cilium CIDR policy; the applications’ LoadBalancer Services use externalTrafficPolicy: Local so the client IP is preserved);
  • no egress out of the cluster by default (the zone’s CiliumClusterwideNetworkPolicy baseline; the namespace’s own NetworkPolicy only closes its ingress): the zone’s platform policy lets application namespaces out to the hub Firewall, which allows only the destinations named in the application’s rule file – Entra ID, PaaS private endpoints, stateful cluster services and approved Internet names (section 5.1); an application writes no egress policy of its own, and Azure Policy rejects one that would open CIDRs or names out of the cluster (section 2.7). Key Vault is never on that list: secrets and certificates arrive as Kubernetes Secrets from the zone’s External Secrets Operator (section 2.5).

Every namespace also has a ResourceQuota and a LimitRange.

Who may work in a namespace

The onboarding request names up to three Entra ID groups per namespace, one for each of Kubernetes’ built-in user-facing roles:

Role Group’s members may They may not In the zone’s Key Vault
view Read most objects in the namespace Read Secrets, roles or role bindings; write anything Nothing
edit view, and write most objects, Secrets included where policy lets anyone (in a zone’s namespaces only External Secrets Operator does) Change roles or role bindings Custom Key Vault Secrets Editor on <namespace>--* only, by name: create and change secrets and read their attributes, never a value; no delete; no listing
admin edit, and roles and role bindings inside the namespace Change the namespace, its quota or its default network policies Key Vault Secrets Officer on <namespace>--* only, by name: also read values, delete, recover and purge; no listing

None of them reaches another namespace or anything cluster-wide. The cluster IaC gives each group a RoleBinding platform-<role> in the namespace, applied by Flux through a configuration <ns>-access of its own (one Kustomization per role), and the Azure role Azure Kubernetes Service Namespace User on the managed namespace, so that its members can fetch a kubeconfig for that namespace alone (az aks namespace get-credentials). On the namespace’s share of the zone vault the same groups get the matching rights (section 2.5): since application secrets reach the cluster only through External Secrets Operator, managing them in the vault is how a team with edit changes its Secrets. A role without a group gets no binding and no vault role. The isolation tests (section 0.3, R13) compare the bindings with the IaC and check each role’s rights – in the cluster and in the vault – by impersonating a member of its group and by signing in as a test application that is one.

None of these groups can list the cluster’s namespaces or the vault’s secrets: other tenants’ names stay hidden from them – soft multi-tenancy, the separation of tenants sharing one cluster and one vault in their data planes (section 2.5, which also applies the pattern to other PaaS services).

Platform viewers

Some tools need to see what the tenants may not: the Azure portal’s Kubernetes views (Workloads, Services and ingresses, …) start from the cluster’s list of namespaces, and a vault browser from the list of secrets. The platform viewers are the Entra ID groups that may list both – names and attributes only, nothing inside a namespace and no secret value. A team member who is also a platform viewer picks the namespace in the portal and sees there what the namespace’s group allows. The environment IaC grants them one custom role, defined and assignable at the environment’s resource group – nothing at the subscription, so it fits a hardened corporate landing zone that keeps subscription-level role definitions to its platform team:

Permission For
Microsoft.ContainerService/managedClusters/read, .../listClusterUserCredential/action The clusters in the portal, a kubeconfig (Entra ID)
Data action Microsoft.ContainerService/managedClusters/namespaces/read Azure RBAC for Kubernetes: get, list and watch namespaces
Microsoft.KeyVault/vaults/read, data action Microsoft.KeyVault/vaults/secrets/readMetadata/action The vaults in the portal, every secret’s name and attributes
Microsoft.Resources/subscriptions/resourceGroups/read The resource group in the portal

The isolation tests check that the role is assignable at the resource group only, and sign in as a test member: it lists the namespaces and every vault’s secret names, and reads nothing in a namespace, no secret value, and writes nothing.

Ownership and cost

Every namespace says who owns what runs in it and who pays for it (R14):

Metadata Example Used by
Label platform/team payments Cost and usage per team (OpenCost, Grafana), alert routing
Label platform/cost-center cc-4711 Charging the team’s share of the cells
Annotation platform/contact payments-team@example.com Whoever finds something wrong with the namespace

The onboarding request names them, and the IaC sets them on the namespace; the platform’s own namespaces are platform. Azure Policy (namespace-ownership, section 2.7) requires them on every namespace but the ones AKS creates. OpenCost (section 6.2) prices every node from Azure’s retail prices and splits its cost over the namespaces by what their pods request and use, so a team’s share of the cell is a query by these labels. A monthly budget on each environment subscription alerts the owners at 80 % and 100 % of the actual cost and at a forecast of 100 % (section 3.2).

OneK8s – a design under review; country codes and IP ranges are examples.Source of this pageComments and issues