Zero-Trust Networking in Kubernetes
Why "inside the cluster" should never mean "trusted", and how identity, encryption, and tight rules turn a wide-open network into one where every connection must prove itself, explained from zero.
Picture an old medieval castle. A thick wall surrounds it, and one big gate lets people in. Once you are past the gate, nobody asks any more questions. You can walk into the kitchen, the armory, the treasury, and the king’s bedroom. The whole defense depends on one idea: the wall keeps the bad people out. Then one day a single thief sneaks in disguised as a delivery driver, and within minutes he has the keys to everything.
For many years, computer networks worked exactly like that castle. A strong firewall guarded the edge, and everything inside was trusted. A default Kubernetes cluster is built the same way. Any pod can talk to any other pod, and nobody checks who is calling. Zero-trust networking throws that old idea away. It says: never trust, always verify. Every single connection, even one between two pods sitting side by side, must prove who it is and show that it is allowed. This post explains how to build that world in Kubernetes, assuming you know nothing about the topic yet. By the end, you will understand every layer, every tool, and every trade-off, in simple language you can use in real projects and in interviews.
1What Zero Trust Really Means
The words “zero trust” are used everywhere, often loosely. Before we touch Kubernetes, we need a precise and simple meaning.
The core idea in one sentence
Zero trust is a security approach in which no user, device, or program is trusted automatically just because of where it is on the network. Every request must be identified, checked against a rule, and allowed only if the rule says yes. The phrase was popularized in the early 2010s by an analyst named John Kindervag, and the United States standards body NIST later published a detailed reference, called Special Publication 800-207, that describes zero-trust architecture in formal terms.
Think of the difference between a castle and an airport. In a castle, once you pass the gate you roam freely. In an airport, your ticket is checked at the entrance, again at security, again at the gate, and again at the plane door. Even staff badges are scanned at every secure door. Zero trust makes your cluster work like the airport: identity is checked at every door, not only the front one.
Four questions behind every request
A zero-trust system asks the same four questions each time one program tries to talk to another.
Identity
Who is making this request? The answer must be provable, not just a claim.
Authorization
Is this caller allowed to do this specific action on this specific target?
Encryption
Is the conversation protected so that nobody in the middle can read or change it?
Visibility
Is the request recorded so that problems can be detected and investigated later?
Three guiding principles
Verify explicitly
Always check identity and context. Never accept “it came from inside the network” as proof.
Use least privilege
Give each program only the connections it truly needs. Everything else is blocked by default.
Assume breach
Design as if an attacker is already inside, so that one stolen pod cannot become a stolen company.
Production example: Google BeyondCorp and BeyondProd
Google publicly described its move away from perimeter trust in a program called BeyondCorp for employee access, and later in a paper called BeyondProd for its production services. The BeyondProd ideas include strong service identity, encrypted service-to-service calls, and rules that say which service may call which. These are the same building blocks you will meet in this post, and they strongly influenced the design of Kubernetes security tools.
Zero trust is not a product you can buy and switch on. It is a design habit, built from several smaller controls that work together: identity, encrypted traffic, tight rules, and constant observation.
2Why a Default Cluster Is a Flat Network
To fix a problem, you must first see it clearly. Kubernetes was designed to make networking simple for developers, and that simplicity is exactly what makes it risky.
The Kubernetes networking promises
Kubernetes sets down a few simple rules. Every pod receives its own IP address. Every pod can reach every other pod using that address, without any address translation in between. Nodes can talk to all pods and pods can talk to all nodes. These rules make life easy, because developers never need to think about ports and tunnels. The cost is that, by default, the cluster is one giant open room.
What a flat network allows
In a flat network, a pod running a small public website can connect directly to the payments database in another namespace, because nothing forbids it. The network does not know that the website has no business talking to the database. It only knows that both have IP addresses.
flowchart LR
I["Internet user"] --> W["Public web pod"]
W --> A["Orders API pod"]
A --> D["Payments database pod"]
W -. "default cluster: also allowed" .-> D
W -. "default cluster: also allowed" .-> S["Internal admin pod"]
W -. "default cluster: also allowed" .-> K["Cloud metadata service"]
Lateral movement: the real danger
Attackers rarely win by breaking into the most valuable system directly. They usually break into the weakest system first, such as an outdated web component, and then move sideways from machine to machine until they reach what they want. Security people call this lateral movement. A flat network makes sideways travel trivial. Many widely reported breaches, including the 2013 retail breach in which attackers entered through a third-party vendor and then crossed an internal network, followed this pattern.
Why teams accept flat networks
- It works immediately, with no configuration.
- Developers never see mysterious connection failures.
- New services can call old ones without paperwork.
What they quietly accept
- One compromised pod can scan the whole cluster.
- Secrets and databases are reachable by every workload.
- Nothing records who talked to whom.
Putting teams in different Kubernetes namespaces does not separate their network traffic. A namespace is only a folder for organizing objects. Without network rules, a pod in one namespace can still talk to a pod in any other.
The attack surface inside a cluster
Other pods
Databases, caches, and message queues often have no password because “only internal apps can reach them”.
Cluster API server
Reachable from every pod. A stolen token here can mean control of the whole cluster.
Node services
The kubelet and the cloud’s metadata endpoint can hand out powerful credentials if reached.
The outside world
Unrestricted outgoing traffic lets an attacker send stolen data anywhere.
3Kubernetes Networking in Plain English
Zero trust is applied to the network, so you need a clear picture of how traffic actually flows in a cluster. We will build the picture one piece at a time.
Pods and their addresses
A pod is the smallest unit Kubernetes runs: one or more containers that share a network identity. Each pod gets one IP address from a range assigned to its machine. Pods come and go constantly, so their addresses change. This is the first reason zero-trust rules cannot be written around IP addresses alone: the address you trusted a minute ago may now belong to a completely different program.
Services: a stable name in front of moving pods
A Service gives a group of pods one stable name and one stable virtual address. Clients call the Service, and Kubernetes spreads the calls across the healthy pods behind it. A component called kube-proxy, or an equivalent built into the network plugin, performs this spreading by rewriting the destination address of each packet to the address of a real pod.
DNS: names instead of numbers
Cluster DNS translates Service names, such as orders.shop.svc.cluster.local, into virtual addresses. Almost every application call begins with a DNS lookup, so DNS is both essential and a favorite place for attackers to hide. Remember this, because it matters when we write egress rules later.
The network plugin
Kubernetes itself does not move packets between machines. It delegates the job to a CNI plugin, where CNI stands for Container Network Interface. Popular choices include Calico, Cilium, and on Amazon EKS the AWS VPC CNI. The plugin gives out pod addresses and wires up the connections. Very importantly for us, the plugin is also the part that enforces network rules. If your plugin does not enforce rules, the rules you write do nothing.
Ingress and egress
Security people use two simple words. Ingress means traffic coming into a pod. Egress means traffic going out of a pod. They also use compass words: north-south traffic crosses the cluster boundary, such as a customer visiting your website. East-west traffic stays inside, such as the website calling the orders service. Zero trust cares about both, but east-west traffic is the part most teams forget.
flowchart TD
U["Customer on the internet"] -->|"north-south"| LB["Load balancer"]
LB --> ING["Ingress or gateway"]
ING --> WEB["Web pods"]
WEB -->|"east-west"| API["Orders API pods"]
API -->|"east-west"| DB["Database pods"]
API -->|"north-south egress"| EXT["External payment provider"]
WEB -.-> DNS["Cluster DNS"]
API -.-> DNS
Think of a large office building. Visitors entering through the main lobby are north-south traffic. Employees walking between departments on different floors are east-west traffic. The receptionist’s phone directory is DNS. The building’s wiring contractor is the network plugin, and the security guards at each floor door are the enforcement points that zero trust adds.
Pod addresses change, Services hide those changes, DNS finds Services, and the network plugin carries and polices the packets. Good zero-trust rules are written using labels and identities, never fixed pod addresses.
4The Zero-Trust Stack for Kubernetes
No single feature gives you zero trust. You build it from layers, each answering one of the four questions from the first chapter.
flowchart TD
L1["Layer 1: Default-deny network rules (NetworkPolicy)"] --> L2["Layer 2: Workload identity (service accounts, SPIFFE)"]
L2 --> L3["Layer 3: Encrypted traffic (mutual TLS)"]
L3 --> L4["Layer 4: Request-level authorization (who may call what)"]
L4 --> L5["Layer 5: Controlled entry and exit (gateways, egress control)"]
L5 --> L6["Layer 6: Cloud and node boundaries (security groups, metadata protection)"]
L6 --> L7["Layer 7: Observability and detection (flow logs, audit, alerts)"]
Reading the stack
| Layer | Question answered | Typical tools |
|---|---|---|
| Network rules | May these two pods connect at all? | NetworkPolicy, Calico, Cilium |
| Workload identity | Who exactly is this program? | Service accounts, SPIFFE and SPIRE, certificates |
| Encrypted traffic | Is the conversation private and authentic? | Mutual TLS from a mesh or the network plugin |
| Request authorization | May this identity perform this exact call? | Mesh authorization policy, API gateway rules |
| Entry and exit control | What may come in, and what may leave? | Ingress, Gateway API, egress gateways |
| Cloud boundaries | Is the underlying network also locked down? | Security groups, private endpoints, metadata protection |
| Observability | Can we see and prove what happened? | Flow logs, mesh telemetry, audit logs |
Why layers instead of one big wall
Each layer covers a weakness of the others. Network rules are fast and simple but only understand addresses and ports. Identity and encryption understand who is calling but depend on a healthy certificate system. Request authorization understands individual API calls but needs a proxy in the path. Cloud boundaries protect against mistakes inside the cluster. Observability tells you when any of the other layers has failed. This design philosophy is called defense in depth.
A bank does not rely on one lock. It has a locked front door, cameras, a guard, a vault door, individual safe-deposit keys, and an alarm. If a thief defeats one, the others still stand. Zero-trust networking stacks protections in the same way.
If you can only do three things this quarter, do these: apply default-deny network rules per namespace, stop pods from reaching the cloud metadata service, and turn on encrypted service-to-service traffic. These three remove most of the easy attack paths.
5NetworkPolicy: The First Wall
Kubernetes gives you one built-in object for controlling pod traffic. It is called NetworkPolicy, and it is the foundation of everything else in this post.
What a NetworkPolicy is
A NetworkPolicy is a rule that lives inside one namespace. It selects a group of pods using labels, and then states which incoming connections (ingress) and which outgoing connections (egress) those pods may have. The “allowed” side can be described by pod labels, by namespace labels, or by ranges of IP addresses called CIDR blocks, together with port numbers.
The most important behavior to understand
NetworkPolicy has one behavior that surprises almost every beginner. Pods start with everything allowed. The moment any policy selects a pod for a given direction, that pod becomes isolated in that direction, and only what the policies explicitly allow is permitted. Policies can only add permissions. They cannot subtract them. There is no “deny” rule in the standard object. If two policies select the same pod, the pod gets the combined allowances of both.
No policy selects the pod
All ingress and all egress are allowed. This is the default, open state.
A policy selects the pod for ingress
The pod now accepts only the incoming connections that some policy allows. Egress is still open.
A policy selects the pod for egress
The pod may now make only the outgoing connections that some policy allows.
More policies are added
Allowances accumulate. A connection works if at least one policy permits it.
Imagine a hotel where every room door is unlocked by default. The first time the manager attaches a guest list to a door, that room becomes private: only people named on any list for that door may enter. Adding more lists adds more names. You can never remove a name by adding a list; you can only edit or delete the list itself.
The three ways to name “who”
Pod selector
Matches pods by labels, such as “app equals orders”. The most common and most flexible choice.
Namespace selector
Matches every pod inside namespaces carrying certain labels, such as “team equals payments”.
IP block
Matches address ranges. Best for things outside the cluster, such as an office network or an on-premises database.
Both together
A namespace selector and a pod selector in one entry means “pods with this label inside namespaces with that label”.
The enforcement catch
The Kubernetes API will happily accept NetworkPolicy objects even if your network plugin does not enforce them. Nothing warns you. A cluster can be full of policies that do absolutely nothing. Always confirm enforcement with a real test: try a connection that should be blocked, and check that it fails.
Plugins that enforce standard network policies include Calico, Cilium, and Antrea. On Amazon EKS, the AWS VPC CNI can enforce them as well when the feature is switched on. Simple plugins that only provide connectivity do not enforce them.
Typical limits of the standard object
What it does well
- Controls connections by label, namespace, address, and port.
- Works on every compliant plugin with the same syntax.
- Is simple to review and store in version control.
What it cannot do
- It has no explicit deny rules or priorities.
- It understands only addresses and ports, not web paths or methods.
- It cannot match by domain name for outside destinations.
- It does not encrypt anything or prove identity.
6Designing Default-Deny Policies
The recipe for a zero-trust namespace is short: close everything first, then open only what the application truly needs. This chapter shows the pattern step by step.
Step one: the default-deny policy
A default-deny policy selects every pod in a namespace and lists no allowed connections. The result is that all ingress, and optionally all egress, is blocked for the whole namespace. Because allowances accumulate, you then add small policies that open exactly the paths you want. Most teams create two default-deny policies per namespace: one for incoming traffic and one for outgoing traffic.
Step two: do not forget DNS
This is the most common mistake. If you block all egress, pods can no longer ask cluster DNS to translate names, and every application call breaks with a confusing “name not found” message. The fix is a small policy that allows pods to send DNS queries, on port 53 over both UDP and TCP, to the cluster DNS pods in the system namespace. Add this as part of the default egress setup so that nobody ever has to debug it twice.
Step three: allow each real path
For every legitimate connection, add one small policy named after what it does. A good habit is to write each rule from the point of view of the receiving pod: “the orders API accepts connections from the web pods on port 8080”. Rules that read like plain sentences are easier to review in code reviews.
flowchart LR
ING["Ingress gateway"] -->|"allow port 8080"| WEB["Web pods"]
WEB -->|"allow port 9090"| API["Orders API"]
API -->|"allow port 5432"| DB["Database"]
API -->|"allow port 443 to payment provider"| EXT["External provider"]
WEB -. "blocked" .-> DB
ING -. "blocked" .-> API
A policy catalog you can reuse
| Pattern | What it does | When to use it |
|---|---|---|
| Deny all ingress | Blocks every incoming connection to the namespace | Every namespace, as the starting point |
| Deny all egress | Blocks every outgoing connection from the namespace | Every namespace that does not need broad internet access |
| Allow DNS egress | Lets pods resolve names through cluster DNS | Always paired with deny all egress |
| Allow same-namespace | Lets pods in one namespace talk to each other | Small, tightly related apps; use sparingly |
| Allow from gateway | Accepts traffic only from the ingress gateway namespace | Front-door web services |
| Allow to database | Opens one port to one database label | Application-to-data-store links |
| Allow monitoring scrape | Lets the metrics system read metrics ports | Every workload that is monitored |
Namespace labels make policies simple
Kubernetes automatically adds a label to every namespace that holds the namespace name. You can use it in namespace selectors to say “pods from the gateway namespace” without inventing labels yourself. For team-level rules, add your own labels, such as the owning team or the environment, and select on those.
Write down a communication map before you write a single policy: a simple table listing which service calls which, over which port. The map becomes your review checklist, your test plan, and your documentation, all in one.
Services versus pods in rules
Network policies are checked against the pods behind a Service, not the Service name. When one pod calls a Service, the packet is rewritten to a real pod address first, and the rule is evaluated against that pod. In practice this means you write rules about pod labels and ports, and the port you allow is the pod’s real container port, which may differ from the Service port. Mixing these up is a classic reason a rule appears correct but still blocks traffic.
Production example: default-deny at a digital bank
Monzo, the UK digital bank that runs a large microservice platform on Kubernetes, has spoken publicly about using network policies to control which services can talk to each other. Their approach illustrates the key idea in this chapter: with hundreds of small services, the only manageable way to limit lateral movement is to describe permitted calls once and block everything else automatically.
7Beyond Basics: Identity-Aware Network Plugins
Standard policies are a strong start, but real systems soon need more: explicit denies, domain names, and rules about web requests. Two popular network plugins add these powers.
Calico: richer policy and priorities
Calico extends the policy model with its own objects. It supports explicit allow and deny rules, rules ordered by priority, policies that apply to the whole cluster rather than one namespace, and policies that protect the machines themselves. This lets a security team set cluster-wide guardrails that application teams cannot override, such as “no pod may ever reach the cloud metadata address”.
Cilium: identity and eBPF
Cilium takes a different approach. Instead of tracking IP addresses, it gives each group of pods with the same labels a numeric security identity, and writes rules in terms of those identities. It enforces them inside the Linux kernel using a technology called eBPF, which lets small, safe programs run directly in the kernel’s packet path. Because the rules follow identities rather than addresses, they keep working correctly as pods come and go, and the checks are very fast.
An address-based rule is like a guard who memorizes people by the seat they sit in. When people change seats, he gets confused. An identity-based rule is like a guard who checks each person’s badge. It does not matter where they sit; the badge tells him who they are.
What identity-aware plugins add
Explicit deny and ordering
Cluster owners can deny first and let application teams allow within those limits.
Domain-name egress
Allow a pod to reach a named service, such as a payment provider, instead of a list of changing IP addresses.
Web-aware rules
Allow only certain HTTP methods and paths, so a pod may read orders but not delete them.
Flow visibility
Cilium’s Hubble component and Calico’s flow logs show every allowed and denied connection.
A standard emerging for cluster-wide rules
The Kubernetes networking community has been developing additional policy objects that let cluster administrators set rules above the namespace level, including baseline defaults and explicit denies. These efforts are still maturing and support differs by plugin, so check what your plugin and version support before relying on them.
Choose standard NetworkPolicy when
- You want portability across plugins.
- Your rules are simple allow lists.
- Your team is new to network security.
Choose plugin-specific policies when
- You need cluster-wide guardrails or explicit denies.
- You need domain-name or HTTP-aware rules.
- You can accept some lock-in to the plugin.
Plugin-specific policy objects do not work if you later change the plugin. Keep your communication map in a neutral format so you can rewrite the rules if you ever migrate.
8Workload Identity: Proving Who You Are
Network rules based on addresses and labels are a good start, but labels can be edited and addresses can be spoofed in some situations. True zero trust needs identities that can be proven with cryptography.
The problem with trusting labels
A label is just a piece of text attached to a pod. Anyone with permission to create or edit pods can write any label they like. If your network rules say “allow pods labeled app equals billing”, then an attacker who can create a pod with that label can borrow the permission. Strong zero trust therefore pairs label-based rules with identities that cannot be forged.
Kubernetes service accounts
Every pod runs as a service account, which is Kubernetes’ built-in way to name a workload. Modern clusters issue each pod a short-lived, signed token tied to its service account, delivered through a mechanism called a projected volume. These tokens expire, are bound to a specific audience, and are rotated automatically by the kubelet. They are good proof of identity to the Kubernetes API server and to any system that can verify them.
Give every application its own service account and turn off automatic mounting of tokens in pods that never call the Kubernetes API. A token that is never mounted can never be stolen from the pod.
SPIFFE: a universal name tag
SPIFFE stands for Secure Production Identity Framework for Everyone. It is an open standard that defines a name format for workloads, called a SPIFFE ID, which looks like a web address with the pattern spiffe://trust-domain/ns/namespace/sa/serviceaccount. It also defines how a workload receives a short-lived certificate proving it owns that name. SPIRE is the most well-known program that implements the standard. Companies such as Uber have publicly described adopting SPIFFE and SPIRE for workload identity across their systems.
flowchart TD
P["Pod starts"] --> A["Local agent checks pod facts: namespace, service account, node"]
A --> S["Identity server verifies the facts"]
S --> C["Short-lived certificate issued with the workload's SPIFFE ID"]
C --> W["Workload presents the certificate when it connects"]
W --> R["Receiver verifies the certificate chain and the ID"]
Why short lifetimes matter
A stolen password works until someone notices and changes it. A stolen certificate that lives for only a day, or even an hour, becomes useless quickly. Zero-trust systems therefore issue certificates with short lifetimes and renew them automatically in the background. Service meshes typically rotate workload certificates about once a day by default, with no human involved.
Cloud identity for pods
Pods also need identities when they call cloud services such as storage buckets and queues. On Amazon EKS you can link a service account to a cloud role, so each pod receives only the permissions that role grants. This is the same least-privilege idea applied to cloud calls, and it prevents a compromised pod from using the machine’s broad permissions.
Strong identity gives you
- Rules that follow the real workload, not a movable label.
- Automatic rotation with no shared secrets.
- A clear audit trail of who called whom.
Strong identity costs you
- A certificate authority that must stay healthy.
- More moving parts to monitor.
- A learning curve for platform teams.
9Mutual TLS: Encrypted and Authenticated Conversations
Network rules decide who may connect. Mutual TLS decides who is really on the other end, and makes sure nobody can eavesdrop or tamper along the way.
Start with ordinary TLS
When you open a secure website, your browser uses TLS, short for Transport Layer Security. The website shows a certificate that proves its identity, and the two sides agree on secret keys to scramble the conversation. In ordinary TLS, only the server proves who it is. The browser stays anonymous.
Now make it mutual
In mutual TLS, usually written mTLS, both sides show certificates. The caller proves who it is, and the receiver proves who it is. After the handshake, the receiver knows with cryptographic certainty which workload is calling, and both know the conversation is private. This is exactly the “verify explicitly” principle applied to service-to-service traffic.
sequenceDiagram
participant C as Client workload
participant S as Server workload
C->>S: Hello and request to connect
S-->>C: Server certificate with its identity
C->>C: Verify server certificate against trusted authority
C->>S: Client certificate with its own identity
S->>S: Verify client certificate and read caller identity
C-->>S: Agree on session keys
C->>S: Encrypted application request
S-->>C: Encrypted response
Ordinary TLS is like calling a bank and having the bank prove who it is, while you stay anonymous. Mutual TLS is like two spies meeting in a cafe who each show a special ID card to the other before saying a single word, and then speak in a private code only they share.
What mTLS gives you
Proven callers
The server learns the caller’s verified workload identity, not just an address.
Encryption in transit
Packets crossing the network are scrambled, so snooping on the wire reveals nothing useful.
Tamper detection
Any change to the data in transit is detected and rejected.
A base for authorization
Rules like “only billing may call payments” become reliable because identity is proven.
The problem mTLS creates
Asking every application team to add certificate handling to every program would be slow, error-prone, and different in every language. Certificates must be requested, renewed, and checked correctly everywhere. This is the reason most organizations hand the job to a shared layer. That layer is a service mesh, or increasingly, the network plugin itself.
Production example: encrypting everything between services
Google’s production network encrypts and authenticates calls between its own services using an internal system, as described in the BeyondProd paper. The lesson for Kubernetes teams is that service-to-service encryption is treated as a baseline platform feature, not something each application team builds on its own.
10Service Meshes: mTLS Without Changing Your Code
A service mesh is a layer of infrastructure that handles secure communication for every service, so developers can focus on business logic.
What a mesh actually is
A service mesh places a small network proxy next to each workload. All traffic into and out of the workload passes through its proxy. The proxies together form the data plane, which does the real work of encrypting, checking identity, and applying rules. A separate set of components called the control plane hands out certificates and distributes policy to the proxies.
flowchart LR
subgraph PodA["Pod A"]
AppA["App container"] --- PxA["Proxy"]
end
subgraph PodB["Pod B"]
PxB["Proxy"] --- AppB["App container"]
end
PxA == "mutual TLS" ==> PxB
CP["Mesh control plane: certificates and policy"] -.-> PxA
CP -.-> PxB
Meet the popular meshes
| Mesh | Approach | Notable traits |
|---|---|---|
| Istio | Envoy proxies, with a newer sidecar-free ambient mode | Very feature-rich; powerful authorization policies; more to learn and operate |
| Linkerd | Lightweight purpose-built proxy | Simple to start; mTLS enabled automatically for meshed workloads |
| Cilium service mesh | eBPF in the kernel with optional shared proxies | Fewer extra containers; one tool for networking and mesh |
| Cloud-managed options | Provider-run control planes or lattices | Less operating work; tied to a cloud vendor |
Sidecars and the new sidecar-free designs
The original mesh design puts one proxy container, called a sidecar, inside every pod. It works well but uses extra memory and CPU in every pod and adds some startup complexity. Newer designs move the work out of the pod. Istio’s ambient mode uses a small shared agent on each node to handle encryption and identity, and adds optional shared proxies only for workloads that need request-level rules. Cilium uses the kernel itself. These designs reduce overhead, though they are younger, so evaluate their maturity for your version.
Sidecar model
- Mature and widely deployed.
- Strong isolation: each pod has its own proxy.
- Uses extra resources in every pod.
Sidecar-free model
- Lower resource cost per pod.
- Fewer pod restarts when upgrading the mesh.
- Newer, with fewer years of production history.
Permissive and strict modes
You cannot flip a whole cluster to encrypted-only in one night. Meshes therefore offer a permissive mode, in which a service accepts both encrypted and plain connections, and a strict mode, in which only authenticated mTLS connections are accepted. The usual path is to start permissive, watch which callers are still using plain traffic, fix them, and then switch to strict one namespace at a time.
Permissive mode is a migration tool, not a destination. A namespace that is permissive still accepts plain connections from an attacker, so it provides little zero-trust value until it is switched to strict.
Production example: Lyft and Envoy
Lyft built the Envoy proxy to handle reliable and observable communication between its many services, and then released it as open source. Envoy later became the data plane for Istio and many other systems, which is why so many meshes share similar concepts and features.
11Request-Level Authorization
Once identity is proven, you can write rules far more precise than “this pod may connect to that pod”. You can say which identity may perform which action.
From connections to requests
A network rule says “the web pods may reach the orders API on port 8080”. An authorization rule says “the web workload may read orders, but only the billing workload may create refunds”. The second rule needs a component that understands web requests, namely the mesh proxy or a gateway. It can examine the caller’s identity, the HTTP method, the request path, and even specific headers before deciding.
Connection arrives
The proxy completes the mTLS handshake and learns the caller’s verified identity.
Request is read
The proxy reads the method and path, such as a request to read an order or to issue a refund.
Policy is evaluated
The proxy checks deny rules first, then allow rules, against identity, method, and path.
Decision is applied
The request is passed to the application, or rejected immediately and logged.
Example rule ideas, in words
Read-only for the website
The web workload may use read methods on the orders service, and nothing else.
Refunds only from billing
Only the billing workload identity may call the refund path.
Admin paths from everyone else
Any caller other than the admin tool is rejected on administrative paths.
Nothing unless named
When any allow rule exists on a service, requests that match no allow rule are rejected.
End users, not only services
Zero trust also covers the human on the other side of the request. A common pattern is for the user to log in through an identity provider and receive a signed token, often a JSON Web Token. The mesh or gateway can validate the token’s signature, check its expiry and audience, and pass the verified claims to the application. Combined with workload identity, the service sees both which service is calling and which person it is acting for.
Imagine a hospital. A nurse badge (workload identity) opens the ward door. But to open a specific patient’s file, the system also checks the doctor’s signed order (user token). Two proofs, two different questions, both required.
Use network policies for coarse blocking, mTLS for identity, and request-level rules for fine control. If the proxy fails or is bypassed, the network rules still stand. If a network rule is too broad, the request-level rule still protects the sensitive path.
Benefits of request-level rules
- Very precise control over what each caller may do.
- Rules read like business policy.
- Rejected calls are logged with identity and reason.
Costs to plan for
- Rules must be kept up to date as APIs evolve.
- Debugging a rejected call needs good tooling.
- Applications should still check permissions themselves.
12Controlling the Front Door: North-South Traffic
The edge of the cluster is where the outside world meets your workloads. Zero trust treats this boundary with extra care, but it does not stop there.
Ingress and the Gateway API
An ingress controller or a gateway receives traffic from outside and routes it to the right Service. Kubernetes has two ways to describe such routes. The older Ingress object is simple and widely supported. The newer Gateway API is more expressive and separates responsibilities: infrastructure teams own the gateway itself, while application teams own the routes attached to it. This separation fits zero trust well, because it limits who can expose what.
What the edge should do
Terminate and re-establish TLS
Decrypt customer traffic at the edge, then re-encrypt it toward the backend so the inside is not left plain.
Authenticate users
Check tokens or sessions before the request ever reaches an application.
Filter and rate-limit
Use a web application firewall and rate limits to block common attacks and abusive clients.
Route only what is published
Expose only the specific paths and services you intend. Everything else returns an error.
Do not let the gateway become a trust shortcut
A common mistake is to treat everything arriving from the gateway as automatically trusted. In a zero-trust design, the gateway is just another workload with its own identity. The backend still authenticates it, still checks the user’s token, and still applies request-level rules. The gateway is allowed to talk only to the services it truly fronts, not to the entire cluster.
Giving the ingress controller broad network access to every namespace “so routing just works” turns it into a perfect stepping stone. A single flaw in the gateway then exposes everything. Restrict it with network policies like any other workload.
Cloud load balancers and the layers behind them
On a cloud such as AWS, a managed load balancer usually sits in front of the gateway. It adds its own protections: network access rules, optional web application firewall, and managed certificates. These cloud features complement the in-cluster rules and form the outer layers of the stack.
flowchart LR
USER["Customer"] --> WAF["Cloud firewall and load balancer"]
WAF --> GW["Gateway with its own identity"]
GW -->|"mTLS and token check"| SVC["Backend service"]
SVC -->|"mTLS and identity rule"| DATA["Data service"]
Production example: layered edges at large retailers
Large e-commerce sites commonly place a content delivery network and a web application firewall in front of their load balancers, then terminate and re-encrypt at the gateway, and then use a mesh for internal calls. A request may be inspected and authenticated at four separate points before it reaches a database, which is exactly the repeated verification that zero trust demands.
13Egress Control: Stopping Data from Leaving
Most teams worry about attackers getting in. Zero trust also worries about what gets out. Outgoing traffic is how stolen data leaves, and how a captured pod calls home to its master.
Why egress is often wide open
Developers want their pods to download packages, call partner APIs, and reach cloud services. Locking this down feels like friction, so most clusters allow all outgoing traffic. That means an attacker who compromises one pod can download hacking tools, contact a remote control server, and upload your customer data to any address on the internet, with no resistance.
The layers of egress control
Default-deny egress
Block all outgoing traffic per namespace, then allow only DNS and the specific destinations needed.
Name-based allow lists
Where the plugin supports it, allow a pod to reach a named external service rather than a list of changing addresses.
Egress gateway
Route all outside calls through a small set of controlled nodes, so one fixed address and one inspection point exist.
Cloud network rules
Use virtual-network firewalls and private endpoints to restrict which destinations are reachable at all.
The egress gateway pattern
An egress gateway is a dedicated workload through which all traffic to the outside must pass. It gives you one place to log, inspect, and allow destinations. It also lets your partners allow-list a single stable address for your whole cluster. Applications cannot reach the internet except through it, because network policies block every other route.
flowchart LR
APP["Application pods"] -->|"allowed"| EG["Egress gateway"]
APP -. "direct internet blocked" .-> NET["Internet"]
EG -->|"approved destinations only"| PART["Partner API"]
EG --> LOG["Central logs and alerts"]
DNS as a hidden exit
Even when ordinary connections are blocked, attackers have been known to hide stolen data inside DNS queries, because DNS is almost always allowed. To limit this, make sure pods use only the cluster DNS service rather than public resolvers, and monitor for unusual volumes of strange-looking name lookups.
A company can have perfect front-door security and still lose secrets if every employee can walk out carrying documents. A mailroom that inspects and logs all outgoing packages is the egress gateway: one controlled exit instead of a hundred unwatched windows.
Start egress control with the workloads that handle the most sensitive data, such as payment and identity services. They usually call very few external destinations, so the rules are short and the security gain is large.
Production example: regulated industries
Financial and healthcare companies that run Kubernetes under strict regulation commonly require every outbound destination to be approved and logged. They often combine namespace-level egress rules, a central egress gateway, and cloud firewall rules, so that no single misconfiguration can open a path to the internet.
14Guarding the Control Plane and the Nodes
The most valuable targets in a cluster are not your applications. They are the Kubernetes control plane and the machines running your pods. Zero-trust networking must protect both.
The API server
The API server is the front door to the entire cluster. Anyone who can talk to it with enough permissions can create pods, read secrets, and change rules. Zero trust applies in three ways. First, limit who can reach it at the network level: keep the endpoint private where possible, or restrict it to approved address ranges. Second, require strong authentication for every caller. Third, use fine-grained role-based access control so that each person and program has only the permissions it needs.
The kubelet
Every node runs an agent called the kubelet, which has its own network port. If that port accepts unauthenticated requests, an attacker who reaches it can run commands in pods. Modern, hardened clusters turn off anonymous access to the kubelet and require the API server to authenticate to it. Managed services configure this securely, but self-managed clusters need a deliberate check.
The data store
Cluster state, including secrets, lives in a data store called etcd. It should accept connections only from the API server, require mutual TLS, and encrypt sensitive data at rest. On managed services the provider handles this for you.
| Component | What attackers want | Zero-trust protection |
|---|---|---|
| API server | Cluster-wide control | Private or restricted endpoint, strong authentication, least-privilege access control |
| Kubelet | Command execution on a node | No anonymous access, authenticated and authorized requests only |
| etcd | Every secret in the cluster | Only the API server may connect, mutual TLS, encryption at rest |
| Node metadata endpoint | Cloud credentials of the machine | Block from pods, require session-protected access |
| Container runtime socket | Control of all containers on the node | Never mount it into ordinary pods |
Pods that call the API server
Some pods, such as controllers and operators, must talk to the API server. Treat them as high-value targets. Give each its own service account with narrow permissions, and use network policies to confirm that only those pods can reach the API server address at all. Most ordinary applications never need to call it, so blocking them closes a major path.
A pod that runs in privileged mode, shares the host network, or mounts the host filesystem can bypass many network and identity controls. Use pod security standards to forbid these settings in normal namespaces and allow them only for carefully reviewed system components.
Production example: public exposure incidents
Security researchers have repeatedly found real clusters whose management dashboards or kubelet ports were reachable from the internet without authentication, leading to attackers installing cryptocurrency miners. These cases are widely reported and share one cause: a management interface trusted its network location. Zero trust removes that assumption.
15Certificates, Authorities, and Rotation
Everything cryptographic in zero trust rests on a chain of trust that begins with a certificate authority. If that foundation is shaky, the whole structure is.
What a certificate authority does
A certificate authority, or CA, is the trusted party that signs certificates. When a workload presents a certificate, the receiver checks whether it was signed by a CA that it trusts. If yes, the identity inside is believed. If the CA is compromised, an attacker can mint a certificate for any identity, so protecting the CA is the single most important job in the system.
A sensible hierarchy
flowchart TD
ROOT["Root CA: kept offline and heavily protected"] --> INT["Intermediate CA: used by the cluster"]
INT --> W1["Workload certificate: orders service"]
INT --> W2["Workload certificate: billing service"]
INT --> W3["Workload certificate: gateway"]
Good practice keeps the root authority offline and uses an intermediate authority for day-to-day signing. If the intermediate is ever compromised, you replace it without having to rebuild trust from scratch. Meshes and identity systems can be set up to use an intermediate issued by your organization’s own root, which lets workload identities connect cleanly to the rest of your company’s trust system.
Automating certificates
Certificates for human-facing websites are usually handled by a Kubernetes add-on called cert-manager, which requests, renews, and installs certificates automatically from public or private authorities. Certificates for workload-to-workload mTLS are typically handled by the mesh or identity system itself. The goal in both cases is the same: no human ever copies a certificate by hand, and nothing expires unnoticed.
Issue
A workload proves its facts and receives a certificate that lives for hours or a day.
Renew
Well before expiry, the agent quietly requests a fresh certificate and swaps it in without dropping connections.
Revoke or expire
If a workload is compromised, short lifetimes mean its certificate soon dies on its own, and emergency steps can cut it off sooner.
Audit
Issuance logs record which identity received what and when, for later investigation.
An expired certificate is one of the most common causes of surprise outages in encrypted systems. If automatic renewal silently fails, every connection starts failing at the same moment. Monitor remaining certificate lifetime and alert long before it reaches zero.
Secrets are part of the network story
Database passwords, API keys, and tokens are credentials that grant network access. Keep them out of container images and plain configuration files. Use a dedicated secret store with access control and audit logging, and prefer short-lived credentials over long-lived ones. Where possible, replace passwords with identity-based access so there is nothing to steal at all.
16The Cloud Layer: Zero Trust on Amazon EKS
Your cluster sits inside a cloud network, and the cloud has its own firewalls and identities. The strongest designs make the cloud layer and the Kubernetes layer agree with each other.
Network policy on the VPC CNI
The AWS VPC CNI assigns pods addresses from your virtual network, and recent versions can enforce standard Kubernetes network policies directly. This means the same policy objects you write for any cluster can control pod traffic on EKS once you enable the feature. As always, confirm enforcement with a real test, and check which policy features your version supports.
Security groups for pods
AWS lets you attach a cloud security group to individual pods on supported machine types. This gives a pod the same kind of firewall rule as any other cloud resource. A typical use is letting only the orders pods reach the database service, with the database security group itself referring to the pod security group. It provides a second, independent barrier outside Kubernetes, which is valuable if someone misconfigures a Kubernetes policy.
Blocking the metadata service
Every cloud machine has a special internal address, called the instance metadata service, that hands out information about the machine, including temporary credentials for the machine’s role. If a pod can reach it, an attacker who takes over the pod may steal those credentials and gain the machine’s broad cloud permissions. On AWS, require the session-based version of the service, known as IMDSv2, and set its hop limit to one so that pod traffic cannot reach it. Also use per-pod roles so pods never need the machine’s role at all.
Several public cloud breaches have started with an attacker tricking an application into making a request to the metadata address, stealing credentials, and then using them to read storage. Blocking pods from the metadata service and moving to per-pod roles closes this path.
Pod-level cloud identities
With per-pod roles
- Each workload receives only the cloud permissions it needs.
- Credentials are short-lived and delivered securely.
- A compromised pod reaches only its own resources.
Without per-pod roles
- Every pod on a node can use the node’s role.
- One flaw exposes storage, queues, and databases.
- Audit logs cannot tell workloads apart.
Private connections to cloud services
Instead of sending traffic to cloud services over the public internet, use private endpoints that keep traffic on the provider’s internal network. Combine them with endpoint policies that state which buckets, tables, or queues may be reached from your network. This turns “can this pod reach the service” into a decision made in two places: Kubernetes and the cloud.
Choosing a mesh on AWS
AWS has announced the retirement of its older managed mesh product, App Mesh, so new designs generally choose an open-source mesh such as Istio or Linkerd, a network-plugin approach such as Cilium, or a service-networking offering from AWS called VPC Lattice. Verify the current status of any managed product before committing to it.
| Control | Where it lives | What it protects |
|---|---|---|
| Network policy | Inside Kubernetes | Pod-to-pod and pod-to-outside connections |
| Security group for pods | Cloud network | Pod access to cloud resources and databases |
| Metadata protection | Cloud machine settings | Machine credentials |
| Per-pod roles | Cloud identity system | Access to storage, queues, and other services |
| Private endpoints | Cloud network | Paths between the cluster and cloud services |
17Seeing Everything: Observability and Detection
A rule that you cannot test or observe is only a hope. Zero trust includes continuous watching, so that failures and attacks become visible quickly.
Three sources of truth
Flow logs
Records of which workload connected to which, allowed or denied. Produced by the network plugin and the cloud network.
Request telemetry
Per-request metrics and traces showing callers, targets, status codes, and delays.
Audit logs
The API server’s record of who changed which policy, role, or secret, and when.
Behavior alerts
Tools that watch what processes do inside containers and flag odd behavior, such as a web server starting a shell.
What to look for
Unexpected denials
A burst of blocked connections from one pod may mean it has been compromised and is probing the network.
New communication paths
A service that suddenly talks to something it never has before deserves a question.
Plain traffic in strict areas
Any unencrypted connection in a namespace that should be strict signals a misconfiguration or an attack.
Policy changes
Edits to network policies or mesh rules outside the normal change process should alert someone.
Unusual egress volume
A sudden jump in outbound data from a database-adjacent pod is a classic sign of data theft.
Discovery before enforcement
Observability has a second, practical use: it tells you what rules to write. Run in an observing mode for a few weeks, collect the flows that really occur, and turn them into the first draft of your policies. Tools exist that read flow data and propose network policies automatically. Always review their suggestions by hand, since they will happily approve a connection that was itself a mistake.
When a connection is blocked, engineers need to know which rule did it. Choose tools that report the matching policy in their logs, and train the on-call team to read those logs first. This single habit removes most of the fear around enforcing strict rules.
Production example: using flow visibility to plan
Teams that adopt Cilium commonly use its Hubble component, and teams that adopt Calico use its flow logs, to see which services really talk to each other before enforcing rules. This turns the hardest part of zero trust, knowing what traffic is legitimate, from guesswork into evidence.
18Rolling It Out Without Breaking Production
The biggest risk of zero trust is not the attacker. It is the well-meaning engineer who turns on strict rules on a Friday afternoon and takes down checkout. A careful rollout plan avoids that.
The phased path
Inventory and map
List the services, their owners, and who calls whom. Use flow data to fill in what documentation forgot.
Choose enforcement tools
Confirm your network plugin enforces policies, and decide whether you need a mesh now or later.
Start with low-risk namespaces
Pilot on a test or internal-tools namespace, so mistakes teach lessons without hurting customers.
Observe, then enforce
Run in permissive or audit mode, review what would be blocked, fix the gaps, and only then switch to strict.
Expand by sensitivity
Move on to namespaces that hold the most sensitive data first, since they gain the most from protection.
Make it the default
Bake default-deny and identity into the template used to create every new namespace.
Manage policy as code
Treat every network policy, mesh rule, and authorization policy as source code. Store it in version control, review it like any other change, test it, and deploy it through an automated pipeline. This approach is often called GitOps. It gives you history, rollback, and a clear answer to the question “who allowed this and why”.
Test the rules, not just the application
Add automated checks that prove the policies work. A good test suite includes both positive checks, confirming that required connections succeed, and negative checks, confirming that forbidden connections fail. Run them after every policy change and on a schedule, because drift and plugin upgrades can quietly change behavior.
Good rollout habits
- Change one namespace at a time.
- Keep a quick rollback ready for every step.
- Tell application teams before and after.
- Write runbooks for the most likely failures.
Rollout mistakes
- Enforcing cluster-wide in a single change.
- Skipping the observation phase.
- Leaving temporary exceptions in place forever.
- Giving no one clear ownership of policies.
Provide teams with ready-made policy templates and a simple request process for new connections. When opening a legitimate connection takes ten minutes instead of ten days, people stop looking for ways around the controls.
19Worked Example: Securing an Online Store
Let us apply everything to one imaginary application, step by step, and watch the open cluster turn into a zero-trust one.
The application
An online store runs in a cluster with five parts. A web service serves the shop pages. An orders service manages carts and orders. A payments service charges cards through an outside provider. A catalog service holds product data. A database stores orders. An ingress gateway receives all customer traffic, and a monitoring system scrapes metrics from every service.
Step one: draw the communication map
| From | To | Why |
|---|---|---|
| Gateway | Web | Customers load pages |
| Web | Orders | Place and view orders |
| Web | Catalog | Show products |
| Orders | Payments | Charge a card |
| Orders | Database | Store orders |
| Payments | Outside provider | Process the payment |
| Monitoring | All services | Collect metrics |
Step two: close everything
Create a default-deny policy for both ingress and egress in the store namespace. Add the DNS allowance so names still resolve. At this moment nothing works, which is exactly the safe starting point.
Step three: open each mapped path
Add one small policy for each row of the table. For example, the orders service accepts connections only from the web workload, and the database accepts connections only from orders. The web service has no path to the database or payments at all, so even a fully compromised web pod cannot reach them.
Step four: add identity and encryption
Enable the mesh in the namespace and move from permissive to strict mode. Now every connection in the table is encrypted and authenticated, and each service knows the verified identity of its caller.
Step five: add request-level rules
On the payments service, allow only the orders identity to call the charge path, and nobody to call administrative paths. On the orders service, allow the web identity to read and create orders, but not delete them.
Step six: control the exit
Allow the payments pods to reach only the payment provider’s name through the egress gateway. Every other pod in the namespace has no route to the internet, apart from DNS.
flowchart TD
CUST["Customers"] --> GW["Gateway"]
GW -->|"mTLS, port 8080"| WEB["Web"]
WEB -->|"mTLS, read and create only"| ORD["Orders"]
WEB -->|"mTLS, read only"| CAT["Catalog"]
ORD -->|"mTLS, charge path only"| PAY["Payments"]
ORD -->|"mTLS, port 5432"| DB["Database"]
PAY -->|"via egress gateway"| EXT["Payment provider"]
MON["Monitoring"] -.->|"metrics port only"| WEB
MON -.-> ORD
MON -.-> PAY
The result in numbers
Before, taking over the web pod opened the door to the database, the payment keys, and the internet. After, the same takeover yields a pod that can speak only to two services, in limited ways, with every attempt logged. The attacker has gone from owning the building to holding a single room key.
20An Attack, With and Without Zero Trust
Theory feels abstract until you watch a realistic attack play out twice: once against an open cluster, once against a zero-trust one.
The attack begins
An attacker finds a flaw in a library used by the web service and gains the ability to run commands inside one web pod. This is the starting point in both stories.
Story one: the flat cluster
Scan
The attacker scans the pod network and finds the database, the orders service, and the monitoring tools, all reachable.
Steal credentials
The attacker reaches the cloud metadata address and obtains the machine’s temporary credentials.
Read data
The attacker connects to the database directly, or uses the credentials to read cloud storage.
Exfiltrate
Data is uploaded to a server on the internet. Nothing blocks it, and nobody is told.
Story two: the zero-trust cluster
Scan fails
Default-deny blocks connections to everything except the two approved services. The scan produces a burst of denials that an alert notices.
Metadata blocked
The metadata address is unreachable from pods, and pods use per-pod roles, so there are no machine credentials to steal.
Limited calls only
The attacker can still call the orders service as the web identity, but request-level rules allow only read and create, and every call is logged.
Exit closed
No route to the internet exists from the web pod. Stolen data has nowhere to go.
Detection and response
The alert team isolates the pod, rotates its identity, and patches the library, long before real damage is done.
Side-by-side comparison
Flat cluster outcome
- Database accessed directly.
- Cloud credentials stolen.
- Data leaves unnoticed.
- Discovered weeks later, if at all.
Zero-trust outcome
- Reach limited to two approved services.
- No credentials available to steal.
- No exit route for data.
- Alerts fire within minutes.
21Costs, Performance, and Trade-offs
Zero trust is powerful, but it is not free. Understanding the costs lets you spend wisely and answer honestly when someone asks “is it worth it”.
Performance overhead
Encrypting every connection and checking identity takes a little time. Modern processors perform encryption very quickly, and long-lived connections pay the handshake cost only once. In practice, the added delay per call is usually small compared to the time applications spend doing their own work, but the effect grows with the number of hops in a request. A request that passes through ten services pays the overhead ten times. Measure with your own traffic before and after, rather than trusting a published number.
Resource overhead
Sidecar proxies consume memory and CPU in every pod. With hundreds of pods, this adds up to real money. Sidecar-free designs lower this cost, as do shared agents per node. Include proxy resources when you size pods, and remember that a proxy without enough CPU becomes a source of latency.
Operational complexity
A mesh and an identity system are two more large systems to upgrade, monitor, and debug. When a call fails, the cause might now be an application bug, a network policy, a certificate problem, or a mesh rule. Teams need good tooling and training to find it fast. Without them, engineers lose trust in the platform and ask for exceptions.
| Cost | What it looks like | How to reduce it |
|---|---|---|
| Latency | Small added delay per hop | Reuse connections, keep call chains short, measure regularly |
| Compute | Proxy memory and CPU in every pod | Right-size proxies, consider sidecar-free modes |
| Complexity | More layers to debug | Strong observability, runbooks, training |
| Failure modes | Expired certificates, control-plane outages | Automatic renewal, alerts, tested recovery plans |
| People time | Policy writing and reviews | Templates, automation, self-service requests |
What happens when the control plane fails?
A key design question is how the data plane behaves if the mesh control plane or certificate system becomes unavailable. A well-designed system keeps existing connections and cached policies working for a while, and certificate lifetimes are long enough to ride out brief outages. But if the outage outlasts those lifetimes, new certificates cannot be issued and connections start to fail. Plan for this by running the control plane in several zones, by testing failure drills, and by choosing certificate lifetimes that balance security against survivability.
Shorter certificate lifetimes improve security but make the system depend more on the issuing service staying healthy. There is no universal best number. Pick a lifetime that your recovery drills show you can survive.
Is it worth it?
For a small internal tool with no sensitive data, perhaps a lighter setup is enough: default-deny policies and a locked-down egress. For systems that handle money, personal data, or regulated information, the answer is almost always yes, because the cost of one serious breach far exceeds the cost of running these controls. A sensible compromise is to apply the full stack to the most sensitive namespaces first and keep the baseline everywhere.
22Beyond Web Traffic: Databases, Queues, and Other Protocols
Most zero-trust examples show web requests. Real systems also move data through databases, message queues, caches, and custom protocols. These need slightly different handling.
Not everything speaks HTTP
A web request has a method and a path, which makes request-level rules easy to write. A database connection or a message queue speaks its own language. A mesh proxy can still encrypt and authenticate these connections as plain streams of data, which is called opaque TCP handling, but it cannot read inside them to say “this user may read this table”. For that kind of rule, you rely on the data service’s own permissions.
| Traffic type | What the mesh or plugin can do | What must come from elsewhere |
|---|---|---|
| HTTP and gRPC | Encrypt, authenticate, allow by method and path | Business-level permissions in the application |
| Database connections | Encrypt, authenticate the calling workload, restrict by port | Table and row permissions inside the database |
| Message queues | Encrypt, authenticate the connecting workload | Topic-level permissions from the broker |
| Caches | Encrypt and restrict who may connect | Passwords or access lists from the cache itself |
| Custom TCP or UDP | Network rules by identity and port | Application-level checks |
Two layers of protection for data stores
Treat every database and queue as protected twice. The first layer is the network and identity layer: only the approved workloads can even open a connection, and the connection is encrypted. The second layer is the data service’s own login and permissions. If either layer is misconfigured, the other still holds. Never rely on “only internal apps can reach it” as the only protection for a data store.
A vault sits behind a guarded corridor, and the vault has its own combination lock. The guard decides who may walk down the corridor. The lock decides who may open the vault. A thief who slips past the guard still cannot open the door, and a thief who knows the combination still cannot get down the corridor.
Stateful applications and stable names
Databases and brokers often run as StatefulSets, where each pod has a stable name and its own storage. Pods inside such a cluster talk to each other directly, for example to copy data between replicas. Zero-trust rules must therefore allow replica-to-replica traffic on the replication ports, and block everything else. Write these rules by label, so that a new replica automatically gains the same access.
Protocol quirks to watch
Protocol sniffing
Meshes often guess the protocol by looking at the first bytes. Some protocols, where the server speaks first, can confuse this, so declare the protocol explicitly on the Service port.
Proxy readiness
If the application starts before its proxy is ready, early connections may fail. Configure the mesh to hold the application until the proxy is up.
Short-lived pods
Batch jobs must be able to stop cleanly even though a sidecar proxy is still running. Check how your mesh handles job completion.
Data stores with their own TLS
Many databases support TLS and client certificates directly. This can replace or complement mesh encryption, at the cost of managing certificates for each store.
For the most sensitive data stores, use both mesh-level mutual TLS and the data store’s own authentication. The overlap is deliberate. It is the zero-trust version of wearing both a belt and suspenders.
Production example: payment data environments
Payment card standards require organizations to segment systems that hold card data from the rest of the network and to encrypt that data in transit across open networks. Companies that process cards on Kubernetes typically give the card-handling namespace its own default-deny rules, its own egress controls, and mandatory encrypted connections, so an auditor can see a clear boundary around the sensitive systems.
23Many Clusters, One Trust Model
Large organizations rarely run just one cluster. They run several for different regions, teams, and environments. Zero trust must work across all of them without becoming a tangle.
Why multiple clusters exist
Teams create several clusters to place workloads close to customers, to survive the loss of an entire region, to separate production from testing, or to give sensitive workloads their own boundary. Each cluster has its own control plane, its own network, and its own certificate authority unless you design otherwise.
Two ways to build trust across clusters
Shared root of trust
- All clusters use intermediate authorities signed by one organization root.
- Workloads in different clusters can verify each other directly.
- Simple model, but the root must be guarded extremely well.
Federated trust domains
- Each cluster keeps its own authority and trust domain name.
- Clusters exchange and trust each other’s public keys.
- More flexible, with more moving parts to manage.
flowchart TD
ROOT["Organization root CA, kept offline"] --> ICA["Intermediate CA for cluster A"]
ROOT --> ICB["Intermediate CA for cluster B"]
ICA --> WA["Workloads in cluster A"]
ICB --> WB["Workloads in cluster B"]
WA |"mTLS through gateways"| WB
Connecting the networks safely
Clusters can be connected in several ways. Some meshes provide gateways that carry encrypted traffic between clusters over the internet or private links. Some network plugins offer a built-in multi-cluster feature that gives workloads in different clusters a shared view of identities. Whichever you choose, the same rules apply: the link must be encrypted, each side must authenticate the other, and the policies must still describe which workload may call which, even across the cluster boundary.
Every link between clusters is a new path for an attacker. Do not connect clusters just because you can. Connect only the services that truly need to talk, and keep a separate, tighter policy for traffic crossing the boundary.
Hybrid and on-premises systems
Many companies still run older systems outside Kubernetes, such as legacy databases or mainframes. Zero trust treats these exactly like any other workload: they get identities where possible, their connections are limited to what is needed, and the traffic between them and the cluster is encrypted and logged. Where the older system cannot do mutual TLS, place a small proxy in front of it that does the work on its behalf.
Decide the trust layout
Choose a shared root or federated domains before you build the second cluster, not after.
Connect through controlled gateways
Use dedicated, monitored entry points between clusters rather than opening whole networks to each other.
Write cross-cluster rules by identity
Name the exact workloads that may cross, not entire clusters.
Test failure scenarios
Practice what happens when the link, a gateway, or one cluster’s authority is lost.
Production example: regional failover
Global services that must keep running when a whole region fails commonly run mirrored clusters in several regions. Their meshes and network plugins use shared trust and cross-cluster gateways so that a service in one region can fail over to a copy in another, while the same identity rules still decide who may call what.
24Proving It to Auditors: Compliance and Governance
Security that you cannot demonstrate is hard to defend. Zero trust gives you both stronger protection and clearer evidence for the people who check your work.
What auditors usually ask
Whatever the regulation, auditors tend to ask the same few questions. Is sensitive data separated from everything else? Is data encrypted while traveling across the network? Who can access the system, and is each access recorded? Can you prove the rules are the ones you say they are? Zero-trust controls map directly onto these questions.
| Auditor’s question | Zero-trust control | Evidence you can show |
|---|---|---|
| Is sensitive data segmented? | Default-deny network policy per namespace | Policy files in version control and passing negative tests |
| Is data encrypted in transit? | Strict mutual TLS | Mesh configuration and reports showing zero plain connections |
| Who can access what? | Workload identity and authorization rules | Rule definitions and per-request logs |
| Are changes controlled? | Policy as code with review | Pull request history and approvals |
| Can you detect misuse? | Flow logs, audit logs, alerts | Retained logs and alert records |
| Is outbound traffic controlled? | Egress rules and gateway | Approved destination list and gateway logs |
Standards and benchmarks that help
NIST zero-trust architecture
A formal description of zero-trust components and principles, useful for design documents and board-level explanations.
CIS Kubernetes Benchmark
A checklist of recommended settings for the control plane, nodes, and policies, with tools that scan against it.
Payment, health, and service standards
Common compliance programs require segmentation, encryption, access control, and logging, all of which this post covers.
Kubernetes hardening guides
Government and vendor guides describe network separation and strong authentication as core hardening steps.
Continuous compliance
Instead of preparing for audits once a year, automate the checks. Run scanners that compare your live cluster to your benchmark every day. Fail deployments that would create a namespace without default-deny rules, using admission controls that reject non-compliant objects. Keep logs for as long as your regulations require. When the auditor arrives, you simply hand over the reports that already exist.
flowchart LR
GIT["Policy in version control"] --> CI["Automated tests"]
CI --> ADM["Admission control blocks non-compliant objects"]
ADM --> LIVE["Live cluster"]
LIVE --> SCAN["Daily compliance scans"]
SCAN --> REP["Reports and alerts for auditors and teams"]
Passing an audit proves you met a checklist on a given day. It does not prove you are safe tomorrow. Use compliance evidence as a by-product of strong engineering, never as the goal itself.
Production example: admission policies as guardrails
Many platform teams use policy engines in the cluster’s admission step to enforce rules such as “every namespace must have a default-deny policy” and “no pod may use the host network”. New objects that break the rules are rejected before they ever run, which turns written security standards into automatic guardrails.
25Common Mistakes and Anti-Patterns
Teams adopting zero trust tend to stumble in the same places. Learn these ahead of time and save months of rework.
What people do
They write network policies on a cluster whose network plugin does not enforce them.
Why it hurts
The cluster looks secure on paper and is completely open in reality. Auditors may even sign off on the paper.
Better approach
Verify enforcement with automated negative tests that try forbidden connections and expect failure.
What people do
They allow traffic based only on pod labels, while many people can create pods with any label.
Why it hurts
An attacker who can create a pod can copy a trusted label and inherit its access.
Better approach
Pair labels with verified workload identity, and restrict who may create pods in sensitive namespaces.
What people do
They block all egress without allowing cluster DNS.
Why it hurts
Every service call fails with name errors, panic follows, and the policy is rolled back and never retried.
Better approach
Include the DNS allowance in the standard namespace template from the very beginning.
What people do
They enable the mesh in permissive mode during migration and never switch to strict.
Why it hurts
Plain connections are still accepted, so an attacker can skip encryption and identity entirely.
Better approach
Set a deadline for each namespace, watch for remaining plain traffic, and enforce strict mode.
What people do
They allow the ingress gateway to reach every namespace and treat its traffic as pre-approved.
Why it hurts
One flaw in the gateway becomes access to the whole cluster.
Better approach
Treat the gateway as an ordinary workload with a narrow list of destinations and its own identity checks.
What people do
They carefully protect traffic coming in, but leave outgoing traffic and the cloud metadata address open.
Why it hurts
Stolen credentials and data can leave freely, which is where many real breaches end.
Better approach
Default-deny egress, block pods from the metadata service, and use per-pod cloud roles.
What people do
They let every team add exceptions, with no review and no cleanup.
Why it hurts
Rules pile up until the default-deny posture is just a long list of allows nobody understands.
Better approach
Assign clear ownership, review changes in version control, and schedule regular cleanups of unused rules.
26Frequently Asked Questions
These questions come up again and again when teams begin their zero-trust journey.
27Summary and Key Takeaways
What to remember
- Location is not trust. Being inside the cluster proves nothing. Every connection should be identified, checked, and allowed only by an explicit rule.
- A default cluster is a flat network. Any pod can reach any other pod, which makes sideways movement easy after a single break-in.
- Start with default-deny. Close ingress and egress per namespace, allow DNS, and open only the connections on your communication map. Verify that your network plugin truly enforces them.
- Prove identity with cryptography. Labels can be copied. Workload identities and short-lived certificates cannot, and mutual TLS turns them into encrypted, authenticated conversations.
- Authorize requests, not only connections. Request-level rules say which identity may perform which action, limiting damage even from a trusted-looking caller.
- Control the exits and the cloud edge. Restrict egress, block pod access to the metadata service, use per-pod cloud roles, and protect the API server and kubelet.
- Observe, roll out gradually, and own the policy. Use flow data to plan, enforce one namespace at a time, treat rules as code, and monitor for denials and certificate health.