Spin up a fresh Kubernetes cluster, deploy a handful of services, and something surprising is already true: every pod can talk to every other pod. Your payments service can reach your logging sidecar, a compromised marketing pod can open a connection to your database, and traffic crosses namespace boundaries without asking permission. This is the flat, allow-all default of Kubernetes network policies and cluster networking, and it is one of the most common gaps we see when teams first move workloads into production. This deep dive is part of our Kubernetes security guide, and it focuses on one lever: using network policies to segment cluster traffic so pods can only reach what they genuinely need.
The problem: Kubernetes networking is flat and allow-all by default
The Kubernetes networking model is deliberately simple. Every pod gets its own IP address, and every pod can reach every other pod's IP directly, with no NAT in between. That simplicity is great for developer velocity and service discovery, but it means the network offers no isolation on its own. There is no implicit firewall between namespaces, no barrier between the frontend tier and the data tier, and nothing stopping a workload from making outbound connections anywhere it likes.
The security consequence shows up during incidents. If an attacker gets code execution in a single pod, that flat network becomes their playground. Lateral movement, reaching internal admin endpoints, and exfiltrating data to an external host are all trivially available because the network never says no. The principle of least privilege, which we apply carefully to identities through Kubernetes RBAC and to workloads through pod security, needs an equivalent at the network layer. That equivalent is the NetworkPolicy.
What a NetworkPolicy is (and why it needs a CNI that enforces it)
A NetworkPolicy is a namespaced Kubernetes resource that describes which connections are allowed to and from a set of pods. You write it declaratively, the same way you write a Deployment or Service, and it lives alongside the workloads it protects.
There is one crucial catch. The Kubernetes API server will happily accept and store a NetworkPolicy, but the API server does not enforce it. Enforcement is the job of your Container Network Interface (CNI) plugin. If the CNI installed in your cluster does not implement network policy, your carefully written rules are inert: they exist as objects but change nothing about actual traffic. Several widely used CNIs enforce policies and several do not, so before you rely on segmentation, confirm that your cluster's networking layer actually applies NetworkPolicy resources. A quick way to gain confidence is to apply a deny rule and verify that the blocked traffic really stops.
The building blocks: podSelector, ingress, egress, and peers
Every NetworkPolicy has a small, consistent anatomy.
podSelector chooses which pods in the namespace the policy applies to, based on their labels. An empty podSelector: {} selects every pod in the namespace, which is exactly what you want for a namespace-wide baseline.
policyTypes declares whether the policy governs Ingress (incoming connections), Egress (outgoing connections), or both. This matters: a policy that lists Ingress but no ingress rules denies all incoming traffic to the selected pods, while leaving egress untouched.
Peers describe the other end of an allowed connection. You have three ways to name a peer:
podSelectormatches pods by label within the same namespace.namespaceSelectormatches whole namespaces by their labels, which is how you allow cross-namespace flows.ipBlockmatches CIDR ranges, useful for on-cluster nodes or external systems, with an optionalexceptlist to carve out ranges.
ports narrow an allowed flow to specific ports and protocols (TCP, UDP, SCTP), so you can permit connections to port 5432 without opening everything else.
A subtle but important detail: within a single rule, multiple peer entries are combined with OR, but a namespaceSelector and podSelector written inside the same peer entry are combined with AND, meaning "pods with this label, but only in namespaces with that label." Getting that distinction right is the difference between a tight rule and an accidentally open one.
The default-deny pattern
The most valuable habit with network policies is to start from deny and open up deliberately, rather than start from open and try to close gaps. This is the default-deny pattern, and it is the foundation of practical microsegmentation.
You begin by denying all traffic in a namespace, then layer explicit allow policies on top. Because network policies are additive, an allow rule anywhere in the namespace grants that flow; there is no "deny" rule that overrides an allow. That additive model is why the baseline deny is expressed as a policy with no allow rules rather than as an explicit block.
Here is a policy that denies all ingress and all egress for every pod in a namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: payments
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
With this in place, nothing gets in or out of any pod in the payments namespace until you say otherwise. That feels aggressive, and it is, but it turns every allowed flow into a conscious decision you can review.
Worked examples
Allow ingress only from a specific app. Suppose an api pod should accept connections only from the frontend pods, and only on port 8080. Once the default-deny is in place, you add:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: payments
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
Now the api pods accept traffic from frontend on 8080 and nothing else, while the default-deny continues to block every other source.
Restrict egress, including DNS. Egress control is where teams most often trip up, because almost every pod needs DNS to resolve service names, and DNS runs in the kube-system namespace. If you apply an egress deny without allowing DNS, your pods break in confusing ways. This policy lets the api pods resolve DNS and reach a database on port 5432, and nothing else:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-egress
namespace: payments
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
The DNS rule is the unglamorous line that keeps egress policies from becoming an outage.
Namespace isolation and multi-tenancy
Namespaces are an organizational boundary in Kubernetes, but by default they are not a network boundary. If you host multiple teams, environments, or tenants in one cluster, network policies are how you make namespaces behave like isolated segments.
A common pattern is to give each tenant namespace a default-deny baseline, then allow ingress only from pods within the same namespace using an empty podSelector inside the ingress from. Cross-tenant traffic is then impossible unless a specific policy, keyed on a namespaceSelector, explicitly permits it. Labeling namespaces consistently (many clusters expose the built-in kubernetes.io/metadata.name label) makes these cross-namespace rules readable and auditable. The result is genuine microsegmentation: each tenant lives in its own network island, with the bridges between islands written down as explicit, reviewable policy.
Limitations to keep in mind
Network policies are powerful, but they are not a complete network security story, and it helps to be honest about the edges.
- L3/L4 only. Standard network policies match on IP addresses and ports. They cannot, on their own, allow "GET /health but not POST /admin," match on hostnames or SNI, or inspect HTTP headers. Layer 7 filtering requires additional tooling such as a service mesh or a CNI that offers L7-aware extensions.
- Namespaced scope. A NetworkPolicy only selects pods in its own namespace. There is no built-in cluster-wide policy object in core Kubernetes, so protecting a large cluster means managing policies across many namespaces, which is a strong argument for templating and automation.
- CNI-dependent. As covered above, enforcement lives in the CNI. Behavior around edge cases and any L7 or cluster-wide features can vary between plugins, so validate against the CNI you actually run.
None of these are reasons to skip network policies. They are reasons to treat segmentation as one layer in a defense-in-depth approach that also includes RBAC, pod security, and image and manifest scanning.
Testing and validating your policies
A network policy you have not tested is a hypothesis, not a control. Because the effect of a policy is "traffic that used to work now fails," the only way to be sure is to exercise the paths.
Start by confirming the negative case: from a pod that should be blocked, try to reach the protected service and verify the connection times out or is refused. A lightweight debug pod running curl, wget, or nc against the target is usually enough. Then confirm the positive case: from a pod that should be allowed, verify the connection succeeds. Do this for both ingress and egress, and remember to test DNS resolution explicitly, since a broken DNS egress rule is the most common silent failure.
Beyond manual checks, keep a small library of connectivity tests you can run after any policy change, and treat an unexpected success (traffic that should be denied but is not) as seriously as an unexpected failure. Because policies are additive, a single overly broad allow rule elsewhere in the namespace can quietly undo an isolation you thought you had.
Shift left: review network policies as code
Network policies are YAML that lives in your repositories, which means they can be reviewed, tested, and gated before they ever touch a cluster. This is where segmentation stops being a day-two firefight and becomes part of how you build.
Treat every policy change like any other code change. Require review on pull requests that modify or add NetworkPolicy manifests, and be especially alert to changes that widen a selector, add an ipBlock with a broad CIDR, or remove a default-deny. In CI, scan your Kubernetes manifests and infrastructure-as-code so that a namespace shipping without a default-deny, or a policy that opens egress to the world, gets flagged automatically rather than discovered during an incident. Catching a missing or over-permissive policy in a pull request is dramatically cheaper than catching it in production, and it keeps the security conversation close to the developers who own the workload.
How Rainforest helps
Rainforest brings network policy review into the same shift-left workflow your team already uses for code. Our infrastructure-as-code and manifest scanning analyzes Kubernetes manifests and IaC in your pipelines, so missing default-deny baselines, overly broad selectors, and risky egress rules surface as findings in the pull request, with the context developers need to fix them. As part of the broader application security testing platform, it connects those cluster configuration checks to the rest of your AppSec program, giving you one place to see and prioritize risk across code and infrastructure.
If you want to see how policy-as-code review fits into your Kubernetes security workflow, book a demo and we will walk through it with your own manifests.
Frequently asked questions
What is a Kubernetes network policy?
A Kubernetes network policy is a namespaced resource that defines which network connections are allowed to and from a group of pods, selected by their labels. It controls traffic at the IP and port level (L3/L4) and is enforced by your cluster's CNI plugin rather than by the Kubernetes API server itself.
Does Kubernetes deny traffic by default?
No. By default Kubernetes networking is flat and allow-all: every pod can communicate with every other pod, including across namespaces, with no restrictions. You only get deny behavior once you apply network policies that select those pods.
What is a default-deny network policy?
A default-deny network policy selects all pods in a namespace (using an empty podSelector) and declares ingress and/or egress policy types with no allow rules, which blocks all matching traffic. You then add explicit allow policies on top for the specific flows each workload needs, since policies are additive.
Do network policies need a special CNI?
Yes. The Kubernetes API server stores NetworkPolicy objects but does not enforce them. Enforcement is handled by the CNI plugin, and not every CNI implements network policy. Confirm your CNI supports and enforces policies, ideally by applying a deny rule and verifying the traffic actually stops.
Can network policies filter by hostname or L7?
Not on their own. Standard Kubernetes network policies operate at L3/L4, matching IP addresses, CIDR ranges, and ports. They cannot match hostnames, SNI, HTTP methods, or paths. Layer 7 filtering requires additional tooling such as a service mesh or a CNI with L7-aware extensions.

Written by
Bruno Baldo
CMO
Um pouco de marketing e um pouco de curiosidade e temos a receita pra criar um apaixonado por cyber!

Kubernetes Security: The Complete Guide
A complete guide to Kubernetes security: the 4C's model, attack surface, RBAC, pods, network policies, secrets, supply chain, and shift-left practices.

Kubernetes Pod Security: Standards, Admission and securityContext
A practical guide to Kubernetes Pod Security: the Pod Security Standards, Pod Security Admission, and the securityContext settings that keep pods locked down.

Kubernetes RBAC: Role-Based Access Control Explained
Kubernetes RBAC explained: how Roles, bindings, and ServiceAccounts work, how access is evaluated, and least-privilege best practices for your cluster.
