Network Policy Enforcement Internals in Kubernetes

Network Policy Enforcement Internals in Kubernetes

A NetworkPolicy is only a note written in the Kubernetes database. Something else has to read that note and turn it into real packet filtering on every machine. This guide follows the journey from the note to the rules inside the network layer, so you understand what really happens when a packet is allowed or dropped.

Imagine an apartment building where every door is unlocked and every resident can walk into every other apartment. Then the owner writes a list of rules on a sheet of paper: “The kitchen on floor two may only be entered by the cook, and the cook may only leave through the back door.” Writing the list does not lock a single door. Only when a security guard reads the list and actually fits locks to the doors do the rules begin to matter. In Kubernetes, a NetworkPolicy is the sheet of paper, and the network plugin on each machine is the guard who fits the locks. This guide is about how that guard works.

1The Problem Network Policies Solve

To appreciate the solution, first look at how open a Kubernetes network is by default.

When you create a cluster, every Pod receives its own network address. Kubernetes expects that any Pod can reach any other Pod directly using these addresses, with no translation in between and no gate on the way. This is a wonderful design for simplicity. Applications do not need to know where their neighbors run, and engineers do not need to open ports one by one.

But a flat, open network is also dangerous. If one Pod is compromised, an attacker who controls it can try to talk to every other Pod in the cluster: databases, internal admin tools, metrics endpoints and anything else that listens on a port. A NetworkPolicy is how you tell the cluster which conversations are allowed and which are not, so that the damage from one broken Pod stays small.

Simple Analogy

An open-plan office lets everyone talk to everyone, which is great for teamwork. But the finance team does not want strangers wandering in. Instead of building walls around the whole office, you give each team a badge reader on its own door. A network policy is that badge reader for Pods.

Term 1

NetworkPolicy

A Kubernetes object that describes which traffic is allowed to reach, or to leave, a group of Pods.

Term 2

Ingress

Traffic that comes into a Pod from somewhere else. Think of it as visitors arriving.

Term 3

Egress

Traffic that goes out of a Pod to somewhere else. Think of it as the Pod making a phone call.

Term 4

CNI plugin

The network plugin that connects Pods to the network and, in many cases, also enforces network policies.

i
Remember

Network policies act at the level of network connections, using addresses and ports. They are one layer of defense and do not replace authentication, encryption or good application design.

2A Refresher on Pod Networking

Enforcement happens inside the pod network, so we need a clear picture of how it is built.

Each Pod runs inside its own network namespace, which is a private copy of the network stack provided by the Linux kernel. Inside it, the Pod has its own network interface, its own address and its own routing table. To connect that private world to the rest of the machine, the system creates a virtual cable called a veth pair. One end sits inside the Pod. The other end sits on the host, where it is attached to a bridge, a router or a special program, depending on the network plugin.

The component that sets all this up is the CNI plugin, where CNI stands for Container Network Interface. When the kubelet starts a Pod, it asks the container runtime to create the namespace, and the runtime calls the CNI plugin. The plugin allocates an address, creates the virtual cable, installs routes, and reports the result. In the same family of plugins, many also run a long-lived agent on every node that watches the cluster and keeps network rules up to date.

flowchart LR
  subgraph Node["One node"]
    P1["Pod A namespace"] --- V1["veth on host"]
    P2["Pod B namespace"] --- V2["veth on host"]
    V1 --- D["Dataplane: bridge, routing, iptables or eBPF"]
    V2 --- D
  end
  D --- NET["Network between nodes"]
        
FIG 1 — Each Pod is connected to the node’s dataplane through a virtual cable

Why the host side of the cable matters

Every packet that leaves a Pod passes through the host end of its virtual cable. Every packet that arrives for a Pod passes through the same place on the way in. This makes the host end of the cable a natural checkpoint. Network plugins that enforce policy usually place their filters here, or close to it, because they can see all traffic of that Pod without changing the Pod itself.

Simple Analogy

Think of every apartment having one front door that opens onto a shared corridor. A guard standing at each front door can see everybody who goes in and out of that apartment, without entering the apartment. The host end of the virtual cable is that front door.

!
Common Misconception

People sometimes think the network between nodes does the filtering. In most designs the filtering is done by software on each node, close to the Pod, long before packets reach the physical network.

3The Key Insight: The API Does Not Enforce Anything

This single idea explains most of the surprises people meet with network policies.

When you create a NetworkPolicy, the API server checks that it is well formed, saves it in the cluster database, and tells anyone who is watching. That is all it does. The API server does not touch the network. The scheduler does not touch the network. The kube-proxy component, which handles Services, does not enforce policies either. Enforcement is the job of the network plugin installed in the cluster.

This has a big consequence. If your plugin does not support network policies, you can create as many NetworkPolicy objects as you like, and the cluster will accept them cheerfully, and nothing will be blocked. The objects exist but nobody is reading them. Several simple network plugins behave this way. A cluster can look fully protected when it is completely open.

“A NetworkPolicy without an enforcing network plugin is a locked-door sign on an unlocked door.”
ComponentRole with network policies
API serverValidates and stores NetworkPolicy objects, notifies watchers
SchedulerNo role
kube-proxyNo role in enforcement. It handles Service address translation only
kubeletNo role in enforcement. It starts Pods and calls the network plugin
Network plugin agent on each nodeWatches policies and Pods, then programs the node’s dataplane to allow or drop traffic
Linux kernel dataplaneActually inspects packets and applies the rules
Simple Analogy

A city council can pass a law that says “no parking here”, but the law does not stop a single car. Traffic wardens and signs on the street do that. The API server is the council. The network plugin on each node is the traffic warden.

i
Good Practice

Right after installing a cluster, test that policies are really enforced. Create a test policy that should block something, then try to reach it. Never assume enforcement just because the objects exist.

4Anatomy of a NetworkPolicy

A policy has only a few parts, and each one has a clear job.

A NetworkPolicy lives inside a namespace. It has four main parts. First, a pod selector that chooses which Pods in that namespace the policy applies to. Second, a list of policy types, which says whether the policy controls ingress, egress or both. Third, a list of ingress rules that describe who may send traffic to the selected Pods. Fourth, a list of egress rules that describe where the selected Pods may send traffic.

Part 1

Pod selector

Chooses the Pods the policy protects, using labels. An empty selector means every Pod in the namespace.

Part 2

Policy types

Says whether the policy is about incoming traffic, outgoing traffic, or both.

Part 3

Ingress rules

Each rule lists allowed sources, called peers, and allowed ports for traffic coming in.

Part 4

Egress rules

Each rule lists allowed destinations, called peers, and allowed ports for traffic going out.

The meaning of policy types

The policy types list controls what the policy takes charge of. If a policy lists ingress, then the selected Pods are restricted for incoming traffic, and only the traffic named in the ingress rules is allowed. If it lists egress, the selected Pods are restricted for outgoing traffic. If you do not write a policy types list, Kubernetes assumes ingress, and also assumes egress when the policy contains egress rules. It is clearer and safer to write the list explicitly.

Simple Analogy

A nightclub bouncer can have two lists: who may come in, and who may leave. If the bouncer has only the “who may come in” list, people can leave as they please. The policy types field says which of the two lists the bouncer is holding.

!
Common Mistake

Writing a policy that has only ingress rules and expecting it to also limit outgoing traffic. It will not. The Pods stay completely open for egress until some policy lists egress for them.

5Peers: Pods, Namespaces and IP Blocks

Rules need a way to name the other side of a conversation, and there are three ways to do it.

Each rule describes its other side, called a peer, in one of three forms. A pod selector names Pods in the same namespace by their labels. A namespace selector names all Pods in the namespaces whose labels match. An IP block names a range of network addresses, written in the common slash notation, with an optional list of ranges to leave out.

Peer typeWhat it matchesTypical use
Pod selector alonePods with matching labels in the same namespace as the policyLet the web tier talk to the database tier in one namespace
Namespace selector aloneAll Pods in every namespace with matching labelsAllow a monitoring namespace to scrape all Pods
Namespace and pod selector togetherOnly Pods with matching labels inside namespaces with matching labelsAllow only the ingress controller Pods from one specific namespace
IP blockAddresses in a range, usually outside the clusterAllow traffic to or from an office network or a cloud service

The classic trap: AND versus OR

The way you write a list of peers decides whether conditions are combined with “and” or with “or”. If the pod selector and the namespace selector are written inside the same list item, both must match, which is an AND. If they are written as two separate list items, either one is enough, which is an OR. The two forms look almost the same in the file, differing only by a small dash, yet they mean very different things. A mistake here often opens the policy much wider than intended.

flowchart TD
  A["Peer list in a rule"] --> B{"Selectors in the same item?"}
  B -->|Yes| C["AND: Pod must match its labels AND live in a matching namespace"]
  B -->|"No, separate items"| D["OR: Either any matching Pod in this namespace OR any Pod in matching namespaces"]
        
FIG 2 — One small dash changes AND into OR
Simple Analogy

“Teachers who work in the science building” is a narrow group. “Teachers, or anyone in the science building” is a much wider one. The words are almost the same, but the second lets in every visitor of the building, and the first does not.

About IP blocks

IP blocks are meant mainly for addresses outside the cluster. Pod addresses change constantly as Pods come and go, so using them in IP blocks is fragile. IP blocks also behave in subtle ways when traffic passes through address translation, such as when it enters through a cloud load balancer. The source address that the cluster sees may not be the original client’s address.

6Ports, Protocols and Port Ranges

A rule can say not only who may talk, but also on which doors the conversation may happen.

Besides peers, a rule can list ports. Each entry gives a protocol and a port number. The supported protocols are TCP, UDP and SCTP, and TCP is assumed if none is written. A port can be given as a number or as the name of a port defined in the Pod’s container settings. Newer versions allow a range, by giving an end port in addition to the starting port.

Rule with peers and ports

Traffic must match one of the peers and one of the ports. A web Pod might be allowed to reach database Pods only on the database port, and nothing else.

Rule with peers only

If ports are omitted, the rule allows all ports for the listed peers. This is easy to forget and can be much wider than intended.

Rule with ports only

If peers are omitted, the rule allows the listed ports from or to anywhere, including outside the cluster.

Empty rule

A rule with neither peers nor ports allows everything. It is the way to write an explicit allow-all.

Simple Analogy

A building pass can say “Maria may enter, only through the side door”. Two ideas are combined: who, and which door. If the pass leaves out the door, Maria may use any door. If it leaves out the name, anyone may use the side door.

i
Good Practice

Always write ports in your allow rules. A rule that lists peers but no ports is legal and often wrong, and it quietly allows far more than the author meant.

7Isolation Semantics and the Additive Model

The rules of combination are simple, and understanding them removes a lot of confusion.

Pods are non-isolated by default. This means all traffic to and from them is allowed. A Pod becomes isolated for ingress the moment at least one policy selects it and lists ingress in its policy types. From then on, only the incoming traffic allowed by the union of all such policies is accepted. Likewise, a Pod becomes isolated for egress when a policy selects it and lists egress.

Policies only add permissions

NetworkPolicy has no deny rules. Every rule is an allow rule. When several policies select the same Pod, the Pod is allowed whatever any one of them allows, so the result is the union of all the rules. Adding one more policy can only make a selected Pod more open, never less. The only way to reduce access is to remove or narrow an allow rule.

SituationResult for incoming traffic
No policy selects the PodAll incoming traffic is allowed
One policy selects the Pod with ingress and no ingress rulesAll incoming traffic is denied
One policy selects the Pod with one allow ruleOnly traffic matching that rule is allowed
Two policies select the PodTraffic matching either policy’s rules is allowed

Default deny patterns

The tool for closing a namespace is a policy with an empty pod selector, which selects every Pod, and with no rules. Written for ingress, it blocks all incoming traffic to every Pod in the namespace. Written for egress, it blocks all outgoing traffic. Written for both, it creates a fully closed namespace. After that, you add small allow policies that open only the paths you need.

Simple Analogy

A guest list for a party works the same way. By default nobody is on the list. Every extra name you add lets one more person in. You can never write “everyone except Paul”, because the list only holds people who are welcome.

!
Common Mistake

Trying to block one bad source by writing a policy that “denies” it. There is no such thing. You must instead remove the allow that lets it in, and close the surrounding area with a default deny.

8How a Single Connection Is Judged

When one Pod tries to talk to another, two separate checks must both pass.

Take a connection from Pod A to Pod B. Two independent questions are asked, one at each end. First, is Pod A allowed to send this traffic out? This is answered by the egress policies that select Pod A. If no policy isolates Pod A for egress, the answer is yes. If one does, there must be an egress rule that matches Pod B, its namespace or address, and the port. Second, is Pod B allowed to receive this traffic? This is answered by the ingress policies that select Pod B, in the same way. Only if both answers are yes does the connection succeed.

flowchart TD
  S["Pod A starts a connection to Pod B"] --> E{"Is A isolated for egress?"}
  E -->|No| I
  E -->|Yes| E2{"Does an egress rule match B and the port?"}
  E2 -->|No| X["Dropped at A's side"]
  E2 -->|Yes| I{"Is B isolated for ingress?"}
  I -->|No| OK["Allowed"]
  I -->|Yes| I2{"Does an ingress rule match A and the port?"}
  I2 -->|No| Y["Dropped at B's side"]
  I2 -->|Yes| OK
        
FIG 3 — The two-sided decision for every new connection

This two-sided nature explains many mysteries. A team may add an ingress rule on the database and find the connection still fails, because the web Pod has an egress policy that does not list the database. The reverse also happens. When you debug, always check both ends.

Simple Analogy

To visit a friend in a secure building, the guard at your building must let you leave, and the guard at your friend’s building must let you in. If either guard says no, the visit does not happen, no matter how friendly the other guard is.

i
Good Practice

When writing policies for a pair of applications, write the egress rule on the client and the ingress rule on the server together, and review them side by side in the same change.

9Statefulness and Connection Tracking

Replies do not need their own rules, because the network remembers who started the conversation.

Network policy rules describe who may start a connection. They do not need to describe the replies. When a connection is allowed, the reply packets that belong to it are allowed automatically, in both directions. This works because the Linux kernel tracks connections in a table, a feature called connection tracking, often shortened to conntrack. Each entry in the table records the addresses, the ports and the state of one conversation.

When a packet arrives, the dataplane first asks conntrack whether this packet belongs to a connection that is already allowed. If yes, the packet passes at once, without any policy check. If not, the packet is a new connection attempt, and the policy rules are consulted. Only new connections are evaluated, which keeps the common case fast.

1

New connection attempt

Pod A sends a first packet to Pod B. Conntrack has no entry for it.

2

Policy check

The egress policy of A and the ingress policy of B are consulted. If both allow it, the packet continues.

3

Entry is recorded

Conntrack stores the new connection as established once B replies.

4

Replies and later packets pass quickly

All later packets of this connection match the table and are accepted without checking the rules again.

Simple Analogy

A receptionist checks your name against the list when you first arrive, and then gives you a visitor badge. After that, you walk around freely and nobody checks the list again until you leave and return. Conntrack is that visitor badge for network connections.

!
Important Detail

Because only new connections are checked, a policy change does not always cut an already established connection immediately. Whether such connections are cut depends on the plugin. Plan for this when you tighten rules on live systems, and restart long-lived connections if you need the new rules to apply at once.

10From Object to Rule: The Enforcement Pipeline

Now we open the black box and follow a policy from the cluster database to the packet filter.

Almost every network plugin that enforces policy has the same basic shape. A small program, often called an agent, runs on every node, usually as a DaemonSet. This agent keeps a live connection to the API server and watches three kinds of objects: NetworkPolicies, Pods and Namespaces. Whenever any of them is created, changed or deleted, the agent hears about it within moments. Some plugins also watch Nodes, Services and their own custom objects.

The agent keeps a local copy of what it has heard, which saves it from asking the API server again and again. When something changes, it follows a simple recipe. It finds which local Pods are selected by which policies. It works out who the allowed peers are for each of them. It translates all this into rules for the dataplane, and it installs only the difference from the last time. This last step is important for speed, because rewriting every rule on every change would be slow and disruptive.

sequenceDiagram
  participant U as User
  participant A as API Server
  participant G as Node agent
  participant D as Node dataplane
  U->>A: Create NetworkPolicy
  A-->>G: Watch event: policy added
  G->>G: Find local Pods selected by the policy
  G->>G: Work out allowed peers from Pods and Namespaces
  G->>D: Install or update filtering rules
  D-->>G: Rules active
  Note over G,D: Later, a Pod label changes
  A-->>G: Watch event: Pod updated
  G->>D: Update only the affected rules
        
FIG 4 — The path from a policy object to an active packet rule
1

Watch

The agent receives events for policies, Pods and Namespaces from the API server.

2

Select

It checks the labels of local Pods against each policy’s pod selector to see who is protected.

3

Resolve peers

It turns each rule’s peers, such as selectors, into concrete addresses or numeric identities.

4

Translate

It converts the result into the language of the dataplane, such as firewall chains or kernel maps.

5

Apply the difference

It updates only what changed, so that running connections and unrelated Pods are not disturbed.

Simple Analogy

A building security manager receives a new access list from head office every time something changes. They do not rebuild the whole security system each time. They look up which doors the change affects, update only those locks, and leave the rest untouched.

i
Good Practice

Because every node watches Pods, labels and Namespaces, a flood of label changes causes a flood of work in the agents. Avoid applications that change labels rapidly, as this can slow down policy updates across the whole cluster.

11From Selectors to Address Sets and Identities

Labels are friendly for humans, but packets only carry addresses, so a translation must happen.

A packet traveling through the network has no labels. It carries a source address, a destination address, a protocol and two port numbers. Yet policies are written using labels and namespaces. Somewhere, a translation must turn “Pods labeled app equals web in the shop namespace” into something that can be matched against a packet. Plugins use two main strategies for this.

Strategy 1

Address sets

The agent builds a list of the current IP addresses of all Pods that match a selector. Rules then say “allow packets whose source address is in this list”. When Pods come and go, the list is updated.

Strategy 2

Identities

Each unique group of labels is given a small number, called an identity. The dataplane records which identity each Pod address belongs to, and rules refer to identities rather than addresses.

Address sets in detail

The address set method is simple and works well on standard Linux tools. The agent keeps one set for each selector that appears in a policy. For example, one set holds all addresses of Pods labeled as the web tier. Each rule then refers to a set by name. When a new web Pod starts, only one address is added to one set, and every rule that uses the set sees the change at once. Without sets, each rule would have to list every address by itself, and a single new Pod would mean rewriting many rules.

Identities in detail

The identity method gives every distinct combination of labels a number. All Pods with the same labels share one identity, no matter how many replicas exist or where they run. The dataplane keeps a table that maps each Pod address to its identity. A policy then says “allow identity 1234 to reach identity 5678 on port 5432”. When a Pod is created, the dataplane only needs to know its identity, and the existing rules apply to it at once. Because the number of identities is much smaller than the number of Pods, the tables stay compact.

AspectAddress setsIdentities
Rules refer toLists of IP addressesSmall numbers standing for label groups
Pod churn costsUpdate the listsUpdate one table entry per Pod
Size grows withNumber of Pods per setNumber of distinct label combinations
Typical homeFirewall-based dataplanesPrograms running inside the kernel
Simple Analogy

A club can check members at the door by reading out every name on a long list, or by giving each member a colored wristband. Address sets are like checking the long list. Identities are like checking the wristband color, which is quicker and does not grow longer as the club grows.

!
Consequence

Since a policy is resolved into addresses or identities at the moment labels are read, changing a label on a Pod can instantly move it in or out of a policy’s scope. Treat label edits on running Pods as security changes, and limit who may make them.

12Internals of Firewall-Based Enforcement

Many plugins use the long-standing Linux firewall tools, and their design shows clever tricks.

The traditional Linux packet filter is called iptables, and its relative nftables is its newer replacement. Both organize rules into chains, which are ordered lists that packets walk through. Packets enter a chain, are compared to each rule in turn, and the first matching rule decides what happens: accept the packet, drop it, or jump to another chain.

A plugin built on these tools creates chains for its own use and hooks them into the places where packets pass. A common arrangement has one entry chain for traffic leaving local Pods, and one for traffic arriving to local Pods. These entry chains jump to a chain specific to each Pod, and the Pod’s chain jumps to the chains of the policies that select that Pod. Each policy chain has rules that mark a packet as allowed if it matches. At the end of the Pod’s chain, any packet that was not marked as allowed is dropped.

flowchart TD
  P["Packet from or to a local Pod"] --> T{"Part of an established connection?"}
  T -->|Yes| A["Accept quickly"]
  T -->|No| C["Pod's own chain"]
  C --> P1["Policy 1 chain: mark allowed if matched"]
  C --> P2["Policy 2 chain: mark allowed if matched"]
  P1 --> M{"Marked as allowed?"}
  P2 --> M
  M -->|Yes| A
  M -->|No| D["Drop the packet"]
        
FIG 5 — A typical firewall-based decision path for a new packet

Why sets are essential

Firewall chains are checked in order, one rule after another. If a policy allowed traffic from five hundred Pods and each address needed its own rule, a packet might have to be compared against five hundred rules before a decision. The cost grows linearly with the number of addresses. Linux offers a tool called ipset, which stores a collection of addresses in a fast lookup structure. A single firewall rule can ask “is the source address in this set?” and receive an answer in nearly constant time. This is why plugins built on these tools keep sets, as discussed earlier.

Advantages

  • Built on mature tools that every Linux administrator knows
  • Easy to inspect with standard commands
  • Works on a very wide range of kernels
  • Sets keep lookups fast even with many Pods

Disadvantages / Trade-offs

  • Chains are walked in order, so very large rule lists slow down
  • Rewriting large rule sets can briefly lock the kernel tables
  • Address-based matching must be updated whenever Pods change
  • Debugging a long web of chains takes patience
Simple Analogy

A security guard with a printed list reads down from the top for each visitor, which is slow if the list is long. A guard with a filing cabinet sorted by surname goes straight to the right drawer. Address sets turn a long printed list into a sorted cabinet.

i
Good Practice

When you inspect a node during an incident, look at the counters on the plugin’s chains. Rules that count dropped packets point straight to the policy that is blocking the traffic.

13Internals of Kernel-Program (eBPF) Enforcement

Some plugins skip the firewall tools and run their own small programs inside the kernel.

The Linux kernel can run tiny, safe programs in response to events, a feature known as eBPF. A network plugin can attach such a program to the host end of every Pod’s virtual cable. Each packet that passes there triggers the program, which looks up information in fast tables called maps and decides what to do with the packet, all inside the kernel and without a long chain of rules.

How a decision is made

In a typical identity-based design, the program first finds the identity of the source and the identity of the destination, using a table that maps addresses to identities. It then looks up a policy table that is keyed by the pair of identities, the port and the protocol. If an entry exists that allows the traffic, the packet is passed on. If not, the packet is dropped. Because these are hash table lookups, the time taken does not depend on how many policies exist. Updating a policy means changing a few table entries, which the kernel can do without disturbing traffic.

sequenceDiagram
  participant Pod as Pod
  participant Prog as Kernel program on the virtual cable
  participant IDs as Address to identity map
  participant Pol as Policy map
  Pod->>Prog: Packet leaves the Pod
  Prog->>IDs: Which identity owns the destination address?
  IDs-->>Prog: Identity number
  Prog->>Pol: Is source identity allowed to reach this identity on this port?
  Pol-->>Prog: Allow or no entry
  Prog-->>Pod: Forward the packet or drop it
        
FIG 6 — An identity-based kernel program deciding on one packet
Benefit 1

Constant-time lookups

Hash maps give answers quickly even with thousands of rules, so large clusters do not slow down as policies grow.

Benefit 2

Rich information per packet

The program can record the reason for a drop and the identities involved, which makes observability much better.

Benefit 3

Extra features

Because the plugin controls the whole path, it can add features such as name-based egress rules and application-level rules by sending selected traffic to a helper proxy.

Cost

Kernel requirements

Newer kernels are needed, and debugging requires plugin-specific tools instead of the familiar firewall commands.

Simple Analogy

Instead of a guard reading a printed list at every door, imagine every door has a small electronic reader that looks up the visitor’s wristband in a fast database and opens or stays shut in a blink. That is the style of a kernel-program dataplane.

!
Important Detail

Different plugins using this technology may differ in which kernel versions they need, how they handle Services and how they report drops. Always read your plugin’s documentation, instead of assuming that all of them behave the same.

14Other Dataplanes: Switch Tables, Windows and Cloud Plugins

Firewalls and kernel programs are the two big families, but they are not the only ones.

Virtual switch based plugins

Some plugins connect Pods to a software switch such as Open vSwitch. Policies become entries in the switch’s flow tables, which match on addresses, ports and protocols and send packets to an accept or drop action. The switch is built to handle many flows quickly and gives detailed counters.

Router and firewall hybrids

Some lightweight plugins combine a simple routing setup with firewall chains and address sets, and use the same watching pipeline described earlier. They are easy to understand and popular for smaller clusters.

Windows nodes

On Windows machines, the host networking service provides access control lists attached to each Pod’s virtual network endpoint. The plugin agent translates policies into those lists. Support differs from Linux, so check the details for your platform.

Cloud provider plugins

Managed Kubernetes services often ship their own networking, and some include policy enforcement, either through a built-in engine or through one of the plugins above. In some services policy support has to be switched on explicitly, and in others it comes with a specific networking mode.

FamilyHow rules are storedHow a packet is judgedTypical strength
Firewall chains and address setsChains in the kernel firewall, plus setsWalk chains, look up setsFamiliar tools, wide kernel support
Kernel programsHash maps read by in-kernel programsLook up identities and policy entriesSpeed at scale and rich observability
Virtual switch flow tablesFlow entries in a software switchMatch flows, apply accept or dropDetailed flow control and counters
Windows access control listsLists attached to endpointsHost networking service evaluates listsNative support on Windows nodes
i
Good Practice

Whatever the family, the semantics of the standard NetworkPolicy object are meant to be the same. But corner cases, such as how external traffic or node traffic is treated, can differ. Test your important cases on your own cluster.

15Where Rules Are Applied on the Path

The place where a rule runs decides which packets it sees and what information it can use.

For a connection from Pod A on node one to Pod B on node two, there are two natural checkpoints. Egress rules for Pod A are applied on node one, close to Pod A, before the packet leaves the machine. Ingress rules for Pod B are applied on node two, close to Pod B, before the packet is delivered. This means each node is responsible only for the Pods that live on it. A node never needs to know the full list of rules for Pods that run elsewhere, apart from the peer information needed to decide on its own Pods.

sequenceDiagram
  participant A as Pod A on node 1
  participant N1 as Node 1 dataplane
  participant NET as Network between nodes
  participant N2 as Node 2 dataplane
  participant B as Pod B on node 2
  A->>N1: Packet to Pod B
  N1->>N1: Check egress policy for Pod A
  N1->>NET: Send, possibly encapsulated
  NET->>N2: Deliver to node 2
  N2->>N2: Check ingress policy for Pod B
  N2->>B: Deliver the packet
        
FIG 7 — Egress is checked at the sender’s node and ingress at the receiver’s node

Encapsulation and the inner address

Many clusters wrap Pod traffic inside another packet when it crosses between nodes, using technologies such as VXLAN or IP-in-IP. From the point of view of the physical network, the traffic is between node addresses. But policies are about Pod addresses, so the enforcement happens either before the packet is wrapped on the sending node, or after it is unwrapped on the receiving node, where the original Pod addresses are visible. A good plugin ensures that policy decisions are always made using the inner, original addresses.

Simple Analogy

A letter inside an envelope travels through the post. The postal workers read only the outer address. But the security desk at the destination opens the envelope and checks the name written on the letter inside. Policy checks look at the letter, not the envelope.

!
Common Mistake

Forgetting that firewalls between nodes in your infrastructure must allow the encapsulation traffic itself. A node-level firewall that blocks it will break Pod traffic in a way that looks like a policy problem but is not.

16Services, kube-proxy and Address Translation

Pods usually talk to Services, not to other Pods, and this affects what the policy engine sees.

A Service gives a stable address to a changing group of Pods. When a Pod connects to a Service address, the component called kube-proxy, or an equivalent built into the network plugin, rewrites the destination from the Service address to the address of one of the real Pods behind it. This rewriting is called destination network address translation, or DNAT.

Network policies are written about Pods, not about Services. In practice, the policy engine evaluates the connection using the real Pod address after the translation has happened. This means that a policy never needs to mention a Service address, and in fact cannot. To allow a client to use a Service, you allow the client to reach the Pods that back the Service, on the port the Pods actually listen on.

1

Client connects to the Service address

The packet leaves the client Pod with the Service address as its destination.

2

Translation picks a backend

The destination is rewritten to the address and port of one backend Pod.

3

Policy sees the real Pod

Egress and ingress rules are evaluated against the backend Pod address and its real port.

4

Replies are translated back

The reply is rewritten so that the client sees it as coming from the Service address.

Simple Analogy

Calling a company’s main phone number connects you to whichever agent is free. The security system at the agent’s desk only cares who you are and which agent you reached. It never asks which main number you dialed.

!
Common Mistake

Writing a rule using the port of the Service when the Pods listen on a different port. The rule must use the Pod’s own port, called the target port, because that is what the policy engine sees after translation.

17Edge Cases: Host Network, Node Traffic and the Outside World

Some traffic does not fit neatly into the Pod-to-Pod picture, and surprises live here.

Edge 1

Pods on the host network

A Pod set to use the node’s own network has no separate address, so policies cannot tell it apart from the node. The standard says behavior is up to the plugin, and many do not apply policies to these Pods.

Edge 2

Traffic from the node itself

Many plugins always allow traffic from a node to its own Pods, so that health probes from the kubelet keep working. Others make this configurable.

Edge 3

Load balancers and node ports

Traffic from outside may be translated so that its source address becomes a node address. IP blocks may then match the node and not the real client, unless the source address is preserved.

Edge 4

Health probes

Probes from the kubelet come from the node, so a strict ingress policy may break them if the plugin does not exempt node traffic.

Edge 5

Traffic inside one Pod

Containers within the same Pod talk through the local address, which is never filtered by policy.

Edge 6

Control plane components

Traffic to the API server, and from admission webhooks and metrics tools, often crosses policy boundaries and must be allowed explicitly.

Simple Analogy

Rules for entering apartments say nothing about the cleaning staff using the master key, or about the people living in the same room. Those cases need separate rules or are simply outside the system, and a careful manager knows exactly which is which.

i
Good Practice

Write down, for your cluster, how each of these edge cases behaves with your chosen plugin. The answers rarely appear in the policy file, but they decide whether an application works after a policy is applied.

18DNS and Egress: The Most Common Surprise

Almost every team that adds egress rules breaks name lookups on the first try.

Applications rarely use raw addresses. They look up names like payments or an external site, and the lookup is a network request to the cluster’s DNS service, normally on port 53, using UDP and sometimes TCP. When you close the egress of a namespace with a default deny, you also block these lookups. Applications then fail with confusing errors, because they cannot even find the names of the services they need, even though the rules for those services are correct.

1

Default deny for egress is applied

Every Pod in the namespace can no longer start outgoing connections.

2

The application tries a name lookup

The lookup request to the DNS service is dropped, so the application waits and then fails.

3

Errors look unrelated

The logs say that a host cannot be found, which seems like a DNS or configuration problem rather than a policy problem.

4

The fix is an explicit allow

Add an egress rule that allows traffic to the DNS Pods in the system namespace, on port 53 for both UDP and TCP.

Simple Analogy

If a company blocks all outgoing phone calls, employees cannot even call the directory service to look up a colleague’s number. They then conclude that the colleague does not exist, when really the phone line was cut.

!
Common Mistake

Allowing only UDP on port 53. Large DNS answers fall back to TCP, and a missing TCP rule causes rare, hard-to-reproduce failures. Allow both.

Names outside the cluster

Standard policies cannot say “allow traffic to example.com”. They know only addresses. The addresses behind a name can change at any time, so listing them in an IP block is fragile. Several plugins add their own extension to allow egress by domain name, which watches DNS answers and allows the addresses it sees. This is a vendor feature and not part of the standard object, so it ties your policies to that plugin.

19Timing, Races and Policy Lag

Enforcement is not instant, and the gaps matter for security and for reliability.

Because the pipeline works by watching and reacting, there is always a short delay between a change in the cluster and a change in the dataplane. This delay is usually small, from milliseconds to a few seconds, but it exists, and it creates a few well-known situations.

A new Pod starts before its rules exist

A Pod may begin to send or receive traffic before its policies are fully installed on the node. Good plugins reduce this window by programming policy for a Pod before they mark its network as ready, but the details differ by plugin.

Peers appear before the other node knows them

A new client Pod may start calling a server whose node has not yet learned about the new client’s address. The first connection attempts can be dropped until the rule sets catch up, and applications with good retry logic hide this.

A policy is removed but old connections linger

Connections already tracked as established may continue until they end, as discussed earlier, even though new connections are now blocked.

Agent restarts

When the agent on a node restarts, there may be a brief period where rules are rebuilt. Plugins try to keep the existing rules in place meanwhile, so traffic is not disturbed.

Control plane slowness

If the API server is overloaded, the agents receive events late, and policy changes take longer to take effect across nodes.

Simple Analogy

When a company changes who may enter a building, the new list takes a little time to reach every guard. During those minutes, one guard may still follow the old list. The system is correct in the long run, but there is a short window of mixed behavior.

i
Good Practice

Build applications to retry connections with a short delay, especially at startup. This protects you from the small windows of policy lag, and from many other transient network problems at once.

20Scale and Performance

The cost of policies depends on how they are written as much as on how many there are.

In a small cluster, network policy costs are invisible. In a large one, with thousands of Pods and hundreds of policies, the choices you make decide how much memory and processor time the agents use, and how quickly changes spread. There are three costs to keep in mind.

CostWhat drives itHow to reduce it
Agent memory and processor timeNumber of Pods, policies, and selectors that must be evaluated on every nodeUse clear labels, few broad selectors, and avoid redundant policies
Update speedHow often Pods, labels and policies changeAvoid label churn, and avoid creating thousands of short-lived Pods with unique labels
Packet processing costLength of rule chains or lookup structure used by the dataplaneChoose a dataplane that uses sets or maps, and keep sets small
Observability overheadLogging every allowed or dropped flowSample, or log only drops, and send logs to a separate system

Designing for scale

The most useful habit is to write policies around stable labels that describe the role of an application, such as its tier or its team, rather than around labels that change with each deployment, such as a version or a build number. Another good habit is to use namespaces as the main unit of grouping, so that rules can say “this namespace may talk to that namespace” instead of listing many separate Pods.

Simple Analogy

A hotel can give access by listing every guest’s name for every door, or by giving each floor a color and saying “blue floor guests may use the blue corridor”. The second approach scales to a thousand rooms without the paperwork growing.

!
Common Mistake

Using a unique label on every Pod, such as a hash or a timestamp, as a selector. Each unique label combination can create a separate set or identity, which increases memory use and update work on every node.

21Namespaces, Tenancy and Policy Ownership

Who may write policies matters just as much as what the policies say.

A NetworkPolicy lives in a namespace and, using the standard rules of access control, anyone who can create policies in a namespace can control the network traffic of Pods in that namespace. This power is useful, because it lets application teams protect their own services. It is also risky, because a team that can write policies can also open them up, for instance by adding an allow-all rule that undoes a security team’s default deny.

Model 1

Platform-owned baseline

A platform team installs the default deny and the DNS allow in every namespace, through automation, and teams add policies on top. Because policies are additive, the baseline cannot be tightened by teams but can be loosened by them.

Model 2

Team-owned policies, reviewed

Teams write their own policies, but changes go through a review in source control, with checks that flag wide rules such as allow-all and missing ports.

Model 3

Admission control as guardrail

An admission rule rejects policies that break company standards, such as policies with empty peers or that open ports to every address.

Model 4

Cluster-wide policies from the plugin

Some plugins provide cluster-scoped policy objects that apply to all namespaces and that team policies cannot override. This is a vendor extension or a newer standard proposal.

Labels on namespaces

Namespace selectors depend on labels on the namespace object. Since version 1.21, Kubernetes automatically gives every namespace a label that holds its own name, which makes it easy to refer to a namespace by name in a selector. Other labels, such as an environment or a team label, are added by people, and anyone who can edit them can change which policies apply. Control who may edit namespace labels.

Simple Analogy

In an apartment building, the owner sets building-wide rules, and each tenant may add rules for their own flat. But if a tenant can also change the labels on the building’s corridors, they might unlock doors that the owner meant to keep locked.

i
Good Practice

Treat namespace labels used by policies as part of your security configuration. Restrict edit rights, and review changes to them as carefully as changes to the policies themselves.

22Rolling Out Default Deny Without Breaking Everything

Closing a busy namespace all at once is how outages happen, so the rollout should be gradual.

The ideal end state is a cluster where each namespace starts closed and only the needed paths are open. Getting there in a running cluster, where nobody has a complete map of who talks to whom, takes care. The safest approach is to learn the traffic first, write the allow rules from what you learned, and only then close the doors.

1

Confirm enforcement works

Test on a scratch namespace that a blocking policy really blocks. Do not plan a rollout on a plugin that does not enforce.

2

Observe real traffic

Use flow logs or an observability tool from the plugin to record which Pods talk to which, on which ports, over a period that includes busy days and scheduled jobs.

3

Draft allow policies from the data

Turn the observed flows into small policies, one per application relationship, always including ports and the DNS allow for egress.

4

Apply allows first, in a test namespace

Add the allow policies before the default deny. Because policies are additive and do nothing until a Pod is isolated, this is harmless.

5

Add default deny for ingress

Ingress is usually safer to close first, because it protects services without breaking how clients make outgoing calls.

6

Add default deny for egress last

Egress is harder to get right because of DNS, external services and control plane calls. Take your time and watch for drops.

Simple Analogy

Before locking all the doors of a busy school, you first watch where children actually walk, give each class a key to the doors it uses, and only then lock the rest. If you lock first and ask questions later, the whole school is stuck in the corridors.

i
Good Practice

Some plugins offer a staging or audit mode for policies, where would-be drops are logged but not enforced. Use it if you have it. It turns a risky change into a safe experiment.

!
Common Mistake

Observing traffic for only an hour. Monthly batch jobs, backups and rarely used admin tools will not show up, and they break on the first day after the policy is enforced.

23Worked Example One: Protecting a Three-Tier Shop

Let us build up the policies for a small online shop, one relationship at a time.

An online shop has three parts in a namespace called shop. A web tier receives customer traffic from an ingress controller that lives in another namespace. An api tier handles the shop’s logic. A database tier stores the data. The team also has a monitoring namespace that scrapes metrics from every tier. The goal is that only the expected conversations happen.

flowchart LR
  ING["Ingress controller namespace"] -->|"Port 8080"| WEB["Web tier"]
  WEB -->|"Port 9000"| API["API tier"]
  API -->|"Port 5432"| DB["Database tier"]
  MON["Monitoring namespace"] -->|"Metrics port"| WEB
  MON -->|"Metrics port"| API
  MON -->|"Metrics port"| DB
  WEB -.->|"Everything else blocked"| DB
        
FIG 8 — The only allowed conversations in the shop namespace
1

Default deny for both directions

A policy that selects all Pods and lists both ingress and egress, with no rules, closes the namespace completely.

2

Allow DNS for everyone

An egress policy selects all Pods and allows traffic to the DNS Pods in the system namespace on port 53, for UDP and TCP.

3

Open the front door

An ingress policy on the web tier allows traffic from Pods of the ingress controller, selected using both the namespace and the pod labels together, on the web port.

4

Connect web to api

An egress rule on the web tier allows the api tier on its port, and an ingress rule on the api tier allows the web tier. Both ends are needed.

5

Connect api to database

Again, an egress rule on the api tier and an ingress rule on the database tier, using only the database port. The web tier gets no path to the database.

6

Allow monitoring

Ingress rules on every tier allow the monitoring namespace to reach the metrics port only, using the namespace name label.

What this achieves

If an attacker takes over a web Pod, the damage is limited. The Pod can reach the api tier on one port and the DNS service, and nothing else. It cannot reach the database, it cannot scan other namespaces and it cannot call out to the internet, because egress is closed. This is the idea of limiting the blast radius of a break-in.

PathAllowed?Reason
Ingress controller to webYesExplicit ingress allow on the web port
Web to api on the api portYesEgress allow on web and ingress allow on api
Web to databaseNoNo egress rule on web for the database, and no ingress rule on database for web
Api to database on the database portYesEgress allow on api and ingress allow on database
Any Pod to the internetNoEgress is closed by default deny
Monitoring to metrics portsYesIngress allow from the monitoring namespace
Another team’s namespace to the shopNoIngress is closed by default deny
i
Good Practice

Name each policy after the relationship it enables, such as web-to-api-egress. Clear names make reviews faster and help the person debugging at three in the morning.

24Worked Example Two: The Policy That Did Nothing, and the One That Did Too Much

Two short stories show the most common mistakes in practice.

Story one: the policy that was never enforced

A team moved to a new cluster and carefully copied every policy from the old one. All the objects were accepted. But a security test later showed that a Pod in the test namespace could reach the production database. The team looked at the policies for days and found nothing wrong. The real cause was outside the policies: the new cluster used a simple networking plugin without policy enforcement. The objects existed, but nothing was reading them.

Lesson

Before trusting any policy, prove enforcement with a deliberate test. Add an automated check to cluster setup that creates a blocked path and confirms that traffic fails.

Story two: the policy that let everybody in

Another team wanted to allow only the ingress controller Pods to reach their web tier. They wrote one rule with two peers: a namespace selector for the ingress namespace, and a pod selector for the controller’s label. They meant “controller Pods that live in the ingress namespace”. But they wrote the two selectors as separate list items, which means OR. The rule therefore allowed every Pod in the ingress namespace, and also every Pod in the shop’s own namespace that carried the controller’s label.

Lesson

Read policies out loud as sentences. “Pods labeled controller in the namespace named ingress” is one item. “Any Pod in the namespace named ingress, or any Pod labeled controller here” is two items. Tests that try a forbidden path would have caught this at once.

flowchart TD
  A["Policy applied"] --> B{"Does a test prove the blocked path fails?"}
  B -->|No| C["Unknown: policy may be unenforced or too wide"]
  B -->|Yes| D{"Does a test prove the allowed path works?"}
  D -->|No| E["Policy may be too narrow and break the application"]
  D -->|Yes| F["Policy is confirmed in both directions"]
        
FIG 9 — A policy is only trustworthy once both allowed and blocked paths are tested
Simple Analogy

A lock is only proven good when you check that the right key opens it and the wrong key does not. Testing only the right key leaves you blind to a lock that opens for everyone.

25Beyond the Standard Object: Vendor Extensions

The standard policy is deliberately small, and many plugins add more power on top.

The standard NetworkPolicy has clear limits. It cannot deny, it cannot log, it cannot match domain names, it cannot target nodes, it cannot apply to the whole cluster and it knows nothing about application protocols such as HTTP. Plugins that offer their own policy objects fill these gaps, and teams often use them for the harder needs.

Gap in the standard policyWhat extensions commonly addTrade-off
No explicit denyRules with allow, deny and sometimes pass actions, evaluated in a set orderOrder and priority must be managed carefully
No cluster-wide scopeCluster-scoped policies that apply across namespacesPowerful, so access to create them must be tightly controlled
No logging actionRules that log matching trafficLog volume can be large
No domain namesEgress rules by fully qualified nameDepends on watching DNS answers and is plugin specific
No application-level rulesRules about HTTP methods and paths, using a helper proxyAdds processing cost and complexity
No node targetingPolicies for traffic to and from the nodes themselvesEasy to cut off the nodes by mistake
No staged modeAudit or staged policies that log without enforcingDifferent plugins use different names and behaviors

Choosing between standard and extended

The standard object is portable. A policy written for one plugin works in another that supports the standard, with the same meaning. Extended objects are not portable, because each plugin defines its own. A good rule is to use the standard policy wherever it is enough, and to reach for extensions only for needs that the standard cannot meet. This keeps most of your policy portable and limits lock-in to a small, clearly marked area.

Simple Analogy

Standard policies are like the common rules that every apartment building follows. Extensions are like special rules of one particular building, such as a doorman who checks IDs. They can be very useful, but they do not move with you when you change buildings.

!
Common Mistake

Mixing standard policies and extended policies without understanding how the plugin orders them. A deny rule in one system and an allow rule in another may combine in ways that surprise you. Read the plugin’s documentation on evaluation order.

26Where Things Are Heading: Cluster-Wide Policy APIs

The Kubernetes community is working on standard objects for the gaps that plugins fill in different ways.

Because many teams needed cluster-wide rules and explicit denies, the Kubernetes network policy working group has been developing additional standard objects. The best-known are the AdminNetworkPolicy and the BaselineAdminNetworkPolicy. At the time of writing they were still evolving and offered through additional components, so check the current status for your version before relying on them.

Object 1

AdminNetworkPolicy

Cluster-scoped rules owned by administrators. They have priorities and can allow, deny or pass a decision on to the namespace-level policies below them.

Object 2

NetworkPolicy

The familiar namespaced object owned by application teams. It sits between the two admin layers.

Object 3

BaselineAdminNetworkPolicy

Cluster-scoped defaults that apply only when nothing else matched, such as a cluster-wide default deny that teams can override with their own allows.

flowchart TD
  T["New connection"] --> A{"Admin policy matches?"}
  A -->|Allow or Deny| R1["Decision made by the administrator"]
  A -->|"Pass or no match"| N{"Namespace policy selects the Pod?"}
  N -->|Yes| R2["Standard rules decide"]
  N -->|No| B{"Baseline policy matches?"}
  B -->|Yes| R3["Baseline decides"]
  B -->|No| R4["Allowed by default"]
        
FIG 10 — The layered model of the newer cluster-wide policy objects
Simple Analogy

A company has building rules from the head office that nobody may break, rules set by each department for its own floor, and sensible defaults that apply where nobody has said anything. Layering the three lets each level do its own job without stepping on the others.

i
Good Practice

Keep an eye on this area. If your plugin supports these objects and they are stable in your version, they can replace many vendor-specific cluster-wide objects and bring some portability back.

27Observability: Seeing Allowed and Dropped Traffic

A policy you cannot observe is a policy you cannot trust or debug.

The standard object has no logging. To see what policies are doing, you rely on the plugin and on the tools of the node. Good observability answers three questions: what traffic is flowing, what is being dropped, and which policy caused each drop.

Source 1

Flow logs from the plugin

Records of connections between workloads, with their identities, ports and verdicts. Many plugins can export these to a log system.

Source 2

Live flow viewers

Command line or web tools that show current flows and drops in real time, useful when debugging a single failing connection.

Source 3

Dataplane counters

Counters on firewall chains or kernel maps show how many packets matched each rule or were dropped.

Source 4

Metrics

Agents expose numbers such as the count of dropped packets, the time to apply a policy change and the number of active policies.

Source 5

Agent logs

Logs show errors in translating policies, which explain why a rule never reached the dataplane.

Source 6

Application errors

Timeouts and refused connections in application logs are often the first sign of a blocked path.

Timeouts versus refusals

A useful clue is how the failure looks to the application. A packet silently dropped by a policy usually causes a timeout, because the sender never gets any answer and keeps waiting. A connection refused error usually means the packet arrived and nothing was listening, or a reset was sent by the destination. When an application reports timeouts to a service that is certainly running, suspect a policy or another filter on the path. When it reports refusals, look at the application and its port first.

What the client seesMost likely causeWhere to look first
Connection timeoutPackets silently dropped by a policy, firewall or routing problemEgress policy on the client and ingress policy on the server
Connection refusedNothing is listening on that port, or the server rejected itThe server Pod, its port and its readiness
Name not foundDNS lookup failed or was blockedEgress rule to DNS on port 53, UDP and TCP
Works from some Pods onlyA policy that selects by labels differs between themLabels on both Pods and the selectors in the policy
Slow first connection, then finePolicy lag or retries on a new PodAgent timing and startup behavior
i
Good Practice

Keep a small debugging Pod image with network tools available for use in a test namespace. Being able to try a connection from a Pod with the same labels as the real client is the quickest way to test a policy theory.

28Testing and Validating Policies

Policies are code that protects your cluster, so they deserve tests like any other code.

A policy file can be valid and still be wrong. Because of that, the most reliable teams treat policies as part of a tested pipeline, with checks at several stages. The aim is to catch mistakes in review, before the change reaches the cluster.

1

Static checks

Automated tools read the policy files and flag risky patterns: policies with no ports, empty peers, allow-all rules and selectors that match nothing.

2

Visual and logical review

Tools can draw the allowed connections as a graph, so reviewers see the real effect of a change, not just the lines of text.

3

Connectivity tests in a test cluster

A suite of simple tests tries every path that must work and every path that must fail, and reports any difference.

4

Staged or audit mode in production

Where supported, run new policies without enforcement and watch what they would have dropped.

5

Continuous checks after release

A small job repeatedly tests a few key paths and alerts if a path that must be blocked becomes open or one that must work breaks.

Positive tests

Confirm that every expected conversation works: client to server on the right port, monitoring to the metrics port, and applications to DNS.

Negative tests

Confirm that forbidden conversations fail: a web Pod to the database, a Pod in one namespace to a service in another, and any Pod to the internet when egress should be closed.

Regression tests

Keep the tests from past incidents, so a mistake that once opened a path cannot return unnoticed.

Simple Analogy

A fire safety inspector does not just read the plan of the building. They walk through, press the alarms and try the doors. Policy testing is the same walk-through for a cluster, done automatically and often.

!
Common Mistake

Running connectivity tests only once, at rollout. Policies and labels keep changing, and a test that passed in March says nothing about the cluster in October.

29A Troubleshooting Guide

Most problems follow a short list of patterns, and a steady method finds them quickly.

SymptomLikely causeWhat to check first
A policy has no effect at allThe plugin does not enforce policies, or enforcement is turned offThe plugin’s documentation, its agent logs and a deliberate test
A new policy blocks everythingAn empty selector or default deny with no allow rulesWhich Pods the policy selects and which allows exist
Rule seems right but traffic failsThe other end has a policy that blocks, or ports do not matchBoth egress on the client and ingress on the server, and the Pod’s own port
Everything fails after adding egress rulesDNS was blockedEgress allow for DNS on port 53, for UDP and TCP
Policy is wider than intendedSelectors written as separate items, meaning ORThe structure of the peer list
Selector matches nothingLabel typo, or missing label on the namespaceLabels on Pods and on Namespaces
Health probes fail after policyNode traffic is not exemptedHow the plugin treats traffic from the node to local Pods
Traffic from outside is blocked or wrongly matchedSource address changed by translationTraffic policy of the Service and the load balancer settings
Rules take long to applyOverloaded agents or API server, or heavy label churnAgent metrics and logs, and recent label changes
Behavior differs between nodesAn agent on one node is failing or outdatedAgent health and version on the odd node

A calm method

Start from the two ends of the failing connection and write down the exact Pods, their labels, their namespaces and the port. Ask whether each end is isolated for the relevant direction. If the client is isolated for egress, find the egress rule that should allow this destination. If the server is isolated for ingress, find the ingress rule that should allow this source. If both seem fine, check the less obvious layers: DNS, translation of Service addresses, firewalls between nodes, encapsulation, and the plugin’s own tools to see the verdict for this exact flow.

Simple Analogy

A detective follows a visitor from the street door to the room they could not enter, and checks each guard along the way. Network debugging follows the packet in the same order, checking each place where a decision is made.

i
Good Practice

Change one thing at a time, and record each change. If you add three allow rules at once and the problem disappears, you will not know which one mattered, and you may have opened more than you need.

30Security Best Practices

Policies are a powerful control, and a few habits make them much more effective.

Practice 1

Default deny everywhere

Close every namespace by default for both directions, with a small baseline of allows such as DNS, then open paths deliberately.

Practice 2

Allow by role, not by address

Use stable labels that describe a workload’s role, and avoid hard-coded Pod addresses, which change all the time.

Practice 3

Always specify ports

Name the exact ports of each rule, so a rule cannot accidentally open every port.

Practice 4

Close egress for sensitive tiers

Databases and internal services rarely need to call the internet. Closing egress stops stolen data from leaving and blocks many attack tools.

Practice 5

Control who edits policies and labels

Use access control to limit who can create or change policies, namespace labels and Pod labels that policies depend on.

Practice 6

Keep policies in source control

Store policies as code, review changes, and deploy through a pipeline so that every change has an author and a reason.

Practice 7

Combine with other layers

Use mutual TLS between services, strong authentication and service accounts with minimal permissions. Network policy limits who can connect, and the other layers limit what a connection can do.

Practice 8

Protect the control plane path

Limit which Pods can reach the API server and the node agents, because these are high-value targets.

“Network policy decides who may knock. It does not decide what happens once the door is open.”
!
Important Detail

Network policy filters at the level of addresses and ports. It does not read the content of the traffic, check identity beyond the Pod’s labels or encrypt anything. An attacker inside an allowed Pod can still misuse an allowed path, which is why layers beyond the network are needed.

31Anti-Patterns to Avoid

These mistakes appear again and again in real clusters, and each has a simple cure.

ANTI-PATTERN-01 Avoid
Problem

Creating policies on a cluster whose network plugin does not enforce them.

Why It’s Harmful

The cluster looks protected while it is wide open, and audits that count policy objects give false comfort.

Correct Approach

Choose a plugin that enforces policies, verify enforcement with a test that must fail, and repeat the test after upgrades.

ANTI-PATTERN-02 Avoid
Problem

Allow rules with peers but no ports, or ports but no peers.

Why It’s Harmful

They open every port to the listed peers, or the listed ports to every address. Both are far wider than people expect.

Correct Approach

Always write both the peer and the port in each rule, and use static checks to flag rules that omit either.

ANTI-PATTERN-03 Avoid
Problem

Closing egress without allowing DNS.

Why It’s Harmful

Every name lookup fails, which breaks applications in confusing ways and wastes hours of debugging.

Correct Approach

Include a baseline egress allow for the cluster DNS service on port 53, for UDP and TCP, in every namespace that has closed egress.

ANTI-PATTERN-04 Avoid
Problem

Using Pod IP addresses in IP blocks to name other Pods.

Why It’s Harmful

Pod addresses change whenever Pods restart, so the policy silently stops matching or matches the wrong Pod later.

Correct Approach

Select Pods by labels and namespaces. Reserve IP blocks for stable external ranges.

ANTI-PATTERN-05 Avoid
Problem

Applying default deny to a live production namespace without learning the traffic first.

Why It’s Harmful

Unknown dependencies, such as batch jobs and monitoring, break at once, and the team scrambles to guess which rules are missing.

Correct Approach

Observe traffic, write the allows first, use a staged mode if available, close ingress before egress, and roll out one namespace at a time.

ANTI-PATTERN-06 Avoid
Problem

Letting anyone edit labels on Pods and namespaces.

Why It’s Harmful

Labels decide which policies apply. A person who can change a label can move a Pod into a more permissive group.

Correct Approach

Restrict label editing through access control, and use admission rules to prevent changes to labels that policies rely on.

32Trade-Offs and Choosing a Network Plugin

There is no best plugin for everyone, only the best fit for your needs.

Advantages of network policies

  • Limit the damage when a Pod is compromised
  • Expressed as simple declarative objects in source control
  • Enforced close to the workload, without changing applications
  • Portable in the standard form across supporting plugins
  • Help meet security and compliance requirements

Disadvantages / Trade-offs

  • No built-in deny, logging, domain names or application-level rules
  • Depend entirely on the plugin to enforce
  • Easy to get wrong, with AND and OR and missing ports
  • Add operational work: testing, observability and rollout care
  • Can slow changes and add resource use at very large scale

Questions to ask when choosing a plugin

QuestionWhy it matters
Does it enforce the standard NetworkPolicy fully, for ingress and egress?Some plugins only support part of the object or only one direction
How does it behave for host-network Pods, node traffic and external traffic?These edge cases decide whether common applications work
What dataplane does it use and what kernel does that need?Affects performance, debugging tools and which machines you can use
What observability does it offer for drops and flows?Debugging policies without flow visibility is slow and risky
Does it offer extended policies, and are they worth the lock-in?You may need explicit denies, names or application rules
How does it scale with thousands of Pods and policies?Important for large and fast-changing clusters
Is it offered or supported by your cloud or distribution?Support, upgrades and defaults are easier when it fits your platform
How does it handle upgrades and agent restarts?You do not want a brief gap in enforcement every time it is updated
Simple Analogy

Choosing a network plugin is like choosing a security company for a building. You compare what they guard, how fast they react, what tools they use and how well they report. The cheapest or most famous company is not always the right one for your building.

i
Good Practice

Try your real policy set on a candidate plugin in a test cluster before committing. The details of edge cases, scale and tooling only become clear when you run your own workloads on it.

33Real-World Uses of Network Policies

The same mechanism supports very different goals.

Multi-team clusters

Many teams share one cluster. A default deny between namespaces means one team’s Pods cannot reach another team’s services unless both agree, which prevents accidental dependencies and limits the impact of a breach.

Regulated data

Applications that handle payment or health data are isolated, with strict ingress and egress rules, so that auditors can see exactly which services are allowed to reach the data.

Protecting databases

Databases accept connections only from the application tier on the database port and may not initiate any outgoing connection, which makes data theft over the network much harder.

Containing untrusted workloads

Platforms that run customer-provided code isolate each customer’s namespace and block access to internal services and cloud metadata addresses, using a mix of standard and extended policies.

Limiting the spread of an intrusion

When a Pod is compromised, tight egress rules stop the attacker from scanning the internal network, from calling attacker-controlled servers and from moving to neighboring services.

Separating environments

Development, staging and production workloads that share a cluster are kept apart, so a test job can never accidentally call a production service.

34Building a Network Policy Program Step by Step

A short, steady program turns scattered policies into a dependable security layer.

Step one: choose and prove the plugin

Pick a network plugin that enforces policies in the way you need, and prove it with tests that must fail. Write down how it treats the edge cases from earlier in this guide, such as node traffic and host-network Pods, so the team shares one set of facts.

Step two: agree on labels and a naming scheme

Policies are only as clear as the labels they use. Agree on a small set of stable labels for the role of each workload, the application it belongs to and the team that owns it. Agree on how namespaces are named and labeled, and on how policies are named.

Step three: write a baseline

Create the standard set that every namespace receives: default deny for ingress and egress, an allow for DNS, and any allows needed for monitoring and health checks. Deliver it through automation, so that new namespaces get it from the first moment.

Step four: add application policies in small pieces

For each application, add one policy per relationship, with exact ports. Keep policies small and named for what they do. Review both ends of each relationship together.

Step five: test, observe and repeat

Put connectivity tests in the pipeline, watch flow data after each rollout and keep a log of incidents and the tests added in response. Review policies regularly, and remove rules that no traffic uses any longer.

DeliverableOwnerPurpose
Enforcement testPlatform teamProves that policies are truly enforced on every cluster
Label and naming guidePlatform and security teamsKeeps selectors consistent and policies readable
Baseline policy setPlatform teamGives every namespace a safe starting point
Application policiesApplication teamsOpen only the paths each application needs
Policy checks in the pipelinePlatform and security teamsCatch wide or broken rules before release
Connectivity test suiteApplication and platform teamsProves allowed paths work and forbidden paths fail
Dashboards and alertsPlatform teamShow drops, policy lag and agent health
Simple Analogy

Building a good security program for a large building does not happen with one big purchase. It happens through steady habits: choosing reliable locks, labeling every door clearly, giving each department its own keys, testing the alarms and learning from every incident.

i
Good Practice

Start with the most sensitive namespaces, such as those holding data stores and payment services. Early wins there deliver the most security for the least effort, and they build the team’s skills for wider rollout.

35Connection Tracking Limits and the Cost on the Packet Path

The connection table that makes policies fast also has limits, and knowing them prevents strange outages.

Earlier we saw that the dataplane remembers each allowed connection in a table. That table lives in the kernel’s memory and has a maximum size. Every new connection through a node adds an entry, and each entry stays until the connection ends or a timer runs out. On a busy node that handles many short connections, such as a gateway Pod serving thousands of clients each second, the table can fill up. When it is full, the kernel starts to drop new packets, and the symptoms look like random connection failures.

Why this looks like a policy problem

A full connection table drops packets without any reference to your policies. Engineers who see sudden timeouts often blame the latest policy change, because it is the most recent thing they touched. In fact, the cause may be an unrelated traffic spike. A quick check of the kernel’s counters for dropped or invalid connections, and of the table’s current size compared to its limit, tells you which one it is.

1

Traffic rises

A promotion or an attack brings many short connections to a few busy nodes.

2

The table fills

Entries pile up faster than they expire, especially for connections that are closed uncleanly and linger until a timer ends.

3

New packets are dropped

The kernel has no room to record a new connection and discards the packet, whatever the policy says.

4

Clients see timeouts

The failures appear random and match the busiest nodes and busiest services, not any particular policy.

Practical limits to know about

Every node has a ceiling on how many connections it can track, and the ceiling depends on the memory given to the kernel table. Nodes that run gateways, proxies or very chatty applications deserve special attention. It is common to give such nodes a higher limit, to spread busy workloads across more nodes, or to dedicate nodes to them so that a traffic storm on one service cannot starve the neighbors.

Timers matter as well. A connection that ends cleanly is removed quickly, but one that is cut off without a proper close can stay in the table for a long time, depending on the timer settings of the kernel. Applications that open and abandon connections carelessly therefore consume table space far beyond their real activity. Fixing the application to close connections properly is often the cheapest way to reduce pressure on the table.

Simple Analogy

A cloakroom with a fixed number of hooks works well for a normal evening. On a very busy night the hooks run out, and new guests are turned away at the door, even though the guest list is fine. The problem is the number of hooks, not the guest list.

The cost of policies on every packet

Policy checks add a small amount of work to the first packet of every connection, and the connection table lookup adds a very small amount to every packet. For most applications this is invisible. For applications that handle enormous numbers of tiny packets or connections, such as some caches, gateways and message brokers, the total can be noticeable, and measuring it on your own hardware is worthwhile before choosing a dataplane.

FactorEffect on the packet pathWhat to do
New connections per secondEach new connection needs a policy check and a table entryReuse connections with keep-alive and connection pools
Connection table sizeA full table drops new packetsWatch table usage and raise the limit on busy nodes if memory allows
Number of rules or lookupsLonger chains cost more per new connection on firewall-based dataplanesPrefer sets, fewer broad rules and a dataplane that scales
Logging every flowAdds work for every connection and large volumes of dataLog drops only, or sample the allowed flows
Encapsulation between nodesAdds a small overhead to each packetAccount for it in the maximum packet size settings
!
Common Mistake

Blaming policies for failures that come with traffic spikes. Before rolling back a policy, check whether the failures follow the policy change or the traffic pattern. Rolling back the wrong thing wastes time and may leave a real security gap.

i
Good Practice

Add node-level alerts for connection table usage and for dropped packets, next to your policy dashboards. Having both views side by side makes it easy to tell a policy problem from a capacity problem.

36Frequently Asked Questions

Short answers to questions that beginners often ask.

Q1Who actually blocks the traffic, the API server or the network plugin?

The network plugin. The API server only stores the policy. An agent on each node reads it and programs the packet filter, so a cluster without an enforcing plugin ignores all policies.

Q2If I create no policy, what is allowed?

Everything. Pods are open for all traffic until at least one policy selects them for a direction.

Q3Can I write a rule that denies traffic?

Not with the standard object. It only holds allow rules, and the result is the union of everything that selects a Pod. You deny by closing the namespace and omitting the allow. Some plugins add explicit deny rules in their own objects.

Q4Do I need rules for reply traffic?

No. Replies to allowed connections are allowed automatically because the dataplane tracks connections.

Q5Why does my application fail after I add an egress policy?

The most common cause is blocked DNS. Allow egress to the cluster DNS Pods on port 53 for both UDP and TCP, and then check the other destinations.

Q6Do policies apply to Services?

No. Policies apply to Pods. The connection is judged using the real Pod address and port after the Service address has been translated.

Q7What is the difference between the two ways to write namespace and pod selectors?

In one list item, both selectors must match, which is an AND. As two separate items, either may match, which is an OR. Mixing them up is one of the most common policy mistakes.

Q8Is a policy applied immediately everywhere?

Almost, but not instantly. Agents react to watch events, so there is a short delay, and established connections may continue for a while depending on the plugin.

Q9Do policies work for Pods on the host network?

Usually not. Such Pods share the node’s address, so they cannot be told apart from the node, and many plugins do not apply policies to them.

Q10How can I allow traffic to a specific website name?

The standard object cannot do this, since it understands only addresses. Some plugins offer extended policies that work by name, but they are not portable.

Q11Does network policy replace encryption or authentication?

No. It limits who can connect. It does not read or protect the traffic and does not prove who a caller really is. Use it together with mutual TLS and strong authentication.

Q12How do I know my policies really work?

Test both sides: confirm that every allowed path works and that every forbidden path fails, in a test cluster and again regularly in production.

37Summary and Key Takeaways

A NetworkPolicy is a declaration that Kubernetes stores and that your network plugin enforces. Each node’s agent watches policies, Pods and Namespaces, turns labels into address sets or identities, and programs the dataplane, whether firewall chains, kernel programs or switch tables. New connections are judged on both ends: egress at the sender and ingress at the receiver. Replies pass through connection tracking. Policies only add allowed paths, so security comes from closing a namespace by default and opening paths with precise peers and ports. Knowing the internals explains the surprises, from silently unenforced policies, to blocked DNS, to selectors that mean OR instead of AND, and it tells you where to look when a connection fails.

Key Takeaways

  • The plugin enforces, not the API — policies do nothing without a network plugin that reads them, so always prove enforcement with a test that must fail.
  • Pods are open until selected — a Pod becomes isolated per direction when a policy selects it, and then only the union of allow rules applies.
  • Both ends decide — a connection needs the client’s egress and the server’s ingress to allow it, so always check both sides.
  • Labels become addresses or identities — agents resolve selectors into sets or numeric identities, so label changes are security changes.
  • Dataplanes differ — firewall chains, kernel programs and switch tables behave differently in speed and tooling, but the standard meaning stays the same.
  • Mind DNS, Services and edges — allow DNS, write rules for real Pod ports, and test host-network, node and external traffic on your own plugin.
  • Roll out with care — observe first, add allows before default deny, close ingress before egress, and keep testing both allowed and blocked paths over time.