Shared Surface · Network

Networking

By default a Kubernetes cluster is one flat network. Every pod can reach every other pod, and the control plane itself becomes a bridge between tenants.

One flat LAN, by default

Out of the box, Kubernetes networking is completely open: every pod can talk to every other pod, across every namespace. Namespaces are an organisational boundary, not a network one. Unless you add NetworkPolicies (and run a CNI that enforces them), Tenant A’s workload can connect straight to Tenant B’s.

Network policies are the main tool for fixing this, and a default-deny policy in each tenant namespace is a good starting point (ideally before any workloads are deployed, as retrofitting them is a lot harder). They do have some limitations to be aware of though:

  • Pods using hostNetwork: true sit outside the CNI’s policy enforcement.
  • Policies that allow traffic based on a custom namespace label can be bypassed by a tenant who can label their own namespace (see the RBAC page).
  • Nothing blocks access to the cloud metadata service (169.254.169.254) by default, so that needs an explicit egress rule.

DNS

Another point to consider for multi-tenant clusters is that DNS is used for service discovery, and by default all workloads in a cluster use the same DNS database to store that information.

As a result, any tenant who can make DNS requests in the cluster will be able to enumerate all the services running in the cluster, and network policies don’t help, as the tenant only needs to talk to the cluster DNS server. CoreDNS-enum is a good example of a brute-force tool which can enumerate every service running in a cluster.

On its own, knowing a service exists doesn’t break the tenant boundary, but it’s a nice bit of reconnaissance for an attacker and could be a privacy issue where tenants are separate customers.

There’s not a great answer to this one. Virtual clusters give each tenant their own DNS, and the Kubernetes multi-tenancy docs mention a CoreDNS policy plugin that can restrict lookups by namespace. I’ve not seen anyone customise their cluster DNS like that in practice though, and I’m not sure how well it would stand up to a brute-force tool or whether it’s even possible on managed Kubernetes.

SSRF by design

Another concern with networking in multi-tenant clusters is that the Kubernetes API server provides a fully-fledged proxy, which is used for managing and troubleshooting applications deployed to the cluster.

As such, the API server will generally be able to reach any workload at the network level, so if a malicious tenant can misuse that feature to connect to other tenants’ workloads, they may be able to bypass network policies.

A good example of this comes from a 2019 blog by Kinvolk. They noted that a user who can edit a pod’s status can change the IP address listed there, and the API server will then use that modified address when proxying requests to the pod. A fix was added to stop the proxy connecting to loopback and link-local addresses (which blocks the cloud metadata service), but any other routable address, including pods in other tenants’ namespaces, is still reachable. This effectively allows for network policy bypass in a multi-tenant cluster.

Additionally, where nodes are shared, there are other SSRF vectors in Kubernetes which go via the Kubelet and could be used by attackers in this type of cluster.

Unpatchable CVEs

There are a couple of Kubernetes CVEs in the networking space that have never been patched, as fixing them would break legitimate functionality. Both are much more of a concern in a multi-tenant cluster than in a single-tenant one.

  • CVE-2020-8554. A tenant who can create Services can set an externalIPs value pointing at any address, and kube-proxy will then redirect traffic from other pods heading for that address to the tenant’s pod. That’s a man-in-the-middle on any other tenant talking to that IP. The fix here is to block ExternalIP services with the DenyServiceExternalIPs admission controller or a policy engine.
  • CVE-2021-25740. Endpoints and EndpointSlices are namespaced, but nothing checks that the IP addresses in them belong to that namespace. A tenant who can write them can point a Service at another tenant’s pod. Traffic then arrives via a shared ingress controller or kube-proxy, which the victim’s network policy is likely set up to trust. Restricting tenant write access to Endpoints and EndpointSlices, or using per-tenant ingress controllers, helps here. N.B. Kubernetes removed this access from the default edit and admin roles in 1.22, but only for clusters created on 1.22 or later. If your cluster has been upgraded from an older version, tenants with those roles probably still have it.

Shared network components

Ingress controllers and service meshes are another shared component that can undo good network policies. ERNW’s Breaking Multi-Tenancy research had a couple of good examples of this:

  • Istio. A VirtualService that uses the special mesh gateway applies its routing rules to every sidecar in the mesh, regardless of the namespace it was created in. So a tenant who can create Istio resources in their own namespace can redirect, intercept or drop other tenants’ traffic (ISTIO-SECURITY-2026-002). The same problem in Kubeflow, where tenants were given Istio edit rights, allowed theft of every user’s session cookie (CVE-2026-47237).
  • Traefik. Traefik blocks cross-namespace references in its CRDs by default, but the same references made via annotations on Ingress objects weren’t covered, letting a tenant attach another namespace’s middleware to their own routes.

The common thread is that anything where a tenant can influence a cluster-wide component is a problem. Annotations are particularly sneaky, as they’re not part of any schema and are often poorly documented.

References & further reading

← All shared surfaces