Kubernetes Network Policies: Practical Pod Security
Implement microsegmentation in Kubernetes using Network Policies to control traffic flow between pods and enforce zero-trust networking.
Kubernetes NetworkPolicy lets teams restrict pod ingress and egress with selectors, protocols, and ports when the network plugin enforces the API. Start by mapping each workload’s dependencies, then isolate traffic and allow only the required paths, including the resolver route used by the cluster. The guide covers standard policy behavior, CNI extensions, testing, observability, and rollout failures so you can validate the rules before production.
Kubernetes Network Policies: Practical Pod Security
By default, all pods in a Kubernetes cluster can communicate with all other pods. This flat network model works during development but creates security risks in production. Without segmentation, a compromised pod may be able to reach other workloads, including databases or in-cluster secrets services. NetworkPolicies can reduce that reach when the CNI enforces them and the selectors cover the relevant traffic.
Kubernetes Network Policies let you restrict pod-to-pod communication based on labels, namespaces, and ports. This microsegmentation approach implements zero-trust networking inside the cluster.
This post covers how Network Policies work, default deny patterns, and practical policy configurations.
For Kubernetes basics, see the Kubernetes fundamentals post. For services and Ingress, see the Services and Networking post.
Introduction
When Network Policies make sense
Multi-tenant clusters often benefit from them. If you do not control what runs in every namespace, a compromised workload may be able to reach a database or another service unless policy and CNI configuration restrict the path.
Compliance programs may call for network segmentation, and NetworkPolicies can contribute to that control. They do not satisfy a framework requirement on their own; map the policy design to the applicable control and validate it with your compliance team.
Network segmentation can limit a pod’s reach, but it does not create zero trust by itself. Restrict the specific paths to databases, caches, and secrets services, and verify both the selectors and the CNI enforcement.
Here is where policies actually earn their keep:
| Scenario | Risk without policies | What a policy does |
|---|---|---|
| Shared cluster with multiple teams | Team A’s pod reads Team B’s database | Allow only explicitly permitted pod-to-pod traffic |
| Sensitive workloads (payments, PII) | Any compromised pod reaches the data layer | Isolate the sensitive namespace from all other workloads |
| Third-party or contractor-deployed apps | Untrusted code reaches internal services | Lock down ingress and egress at the namespace level |
| Production vs. development co-location | Development bugs affect production services | Apply production-grade policies to production namespaces only |
In a shared cluster, a policy such as allow-api-to-db can restrict which selected pods reach the database. Check the policy’s pod and namespace selectors: a new pod with matching labels may also be allowed, and another applicable policy can widen the allowed traffic.
NetworkPolicies can provide evidence of workload-level segmentation, but they are only one part of a compliance control. Whether they apply to a particular PCI DSS, SOC 2, or HIPAA requirement depends on the system scope and the organization’s control mapping. Pair policy manifests with change records, enforcement evidence from the CNI, and the network controls outside the cluster.
Without a policy that isolates the database, a frontend pod may be able to connect to it directly on port 5432. A database ingress policy can allow the API pods and block that direct path, provided the CNI enforces the rule and the selectors match. This reduces one route for lateral movement; it does not prevent every path to the database.
When to skip them
A single-tenant cluster with tightly controlled deployments may have lower risk, but trust in the deployers does not remove compromised dependencies or accidental exposure. Weigh the isolation benefit against the policy operations and debugging burden.
External segmentation can be enough. Cloud VPC security groups that isolate your Kubernetes nodes from each other and from other services provide some protection. Network policies then add defense in depth.
Early development is not the time. The operational overhead of debugging why your service cannot reach its database when you forgot to allow port 5432 slows down iteration.
Traffic Filtering Flow
flowchart TD
P1[Pod A<br/>app=web-frontend] -->|Egress| NP1{Network Policy<br/>on Pod A}
NP1 -->|Allow to<br/>DNS| DNS[CoreDNS<br/>:53]
NP1 -->|Allow to<br/>:8080| P2[Pod B<br/>app=api-backend]
NP1 -->|Block all<br/>else| X[Dropped]
P2 -->|Ingress| NP2{Network Policy<br/>on Pod B}
NP2 -->|Allow from<br/>web-frontend| P1
NP2 -->|Block all<br/>else| X2[Dropped]
NetworkPolicies select pods and isolate ingress or egress separately. Where the network plugin enforces the API, traffic in an isolated direction is generally limited to the union of matching allow rules, with exceptions such as traffic from a pod’s node described by the API.
How Network Policies Work
A Network Policy is a namespaced resource that selects pods and defines ingress and egress rules. The network plugin or CNI implementation enforces the rules. The Kubernetes API accepts the resource even when the installed plugin does not enforce NetworkPolicy, so verify support in the cluster.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-backend-policy
namespace: production
spec:
podSelector:
matchLabels:
app: api-backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: web-frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
This policy allows the api-backend pod to receive traffic from matching web-frontend pods on port 8080 and send traffic to matching postgres pods on port 5432. The egress selector is namespace-local because the policy and both pod selectors are in production.
Policy evaluation order
Network Policies are additive. If multiple policies select the same pod, the union of all allowed traffic is permitted. This means you must carefully design policies to avoid unintended exposure.
Standard Kubernetes NetworkPolicy has no priority or explicit deny rule: allowed traffic is the union of all matching policies. Calico’s native policy API has an order field and explicit actions, but that behavior belongs to Calico resources, not the standard API:
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
spec:
order: 100
In Calico-native policies, lower order values are evaluated first. Check the Calico documentation for how those resources interact with standard NetworkPolicy in your version.
Default Deny All Ingress and Egress
Start with a default deny policy for each namespace, then explicitly allow required traffic. This follows the principle of least privilege.
Default deny ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
podSelector: {} selects all pods in the namespace. With no ingress rules, all incoming traffic is blocked.
Default deny egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
This blocks all outgoing traffic until you add policies allowing specific destinations.
Combined default deny
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Apply this before deploying any application, then add allow policies as you deploy services.
Allowing Specific Traffic with Pod Selectors
After setting default deny, allow specific traffic patterns:
Web frontend to API backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-to-api
namespace: production
spec:
podSelector:
matchLabels:
app: api-backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: web-frontend
ports:
- protocol: TCP
port: 8080
API backend to database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-to-db
namespace: production
spec:
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: api-backend
ports:
- protocol: TCP
port: 5432
Application layer filtering
For more complex rules, use namespaceSelector to allow traffic from specific namespaces:
ingress:
- from:
- namespaceSelector:
matchLabels:
name: frontend
podSelector:
matchLabels:
app: web-frontend
- namespaceSelector:
matchLabels:
name: monitoring
podSelector:
matchLabels:
app: prometheus
This allows traffic from frontend namespace pods labeled app: web-frontend and from monitoring namespace pods labeled app: prometheus.
Namespace-Level Policies
Apply policies at the namespace level to protect entire namespaces or enforce compliance requirements:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: production-isolation
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: production
- namespaceSelector:
matchLabels:
name: ingress-nginx
This allows traffic only from pods in the production namespace or from the ingress-nginx namespace.
Isolating system namespaces
System components are not deployed uniformly. CoreDNS commonly runs in kube-system, while the API server may run outside the pod network and kube-proxy may be replaced by the CNI. A blanket policy on this namespace can disrupt node-originated, control-plane, or monitoring traffic, and standard NetworkPolicy behavior for hostNetwork pods is undefined. Inventory the actual endpoints and test the rules with your CNI before applying isolation.
This example allows ingress from pods in kube-system; it is only a starting point and may not preserve node or control-plane traffic in your cluster:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-system-namespaces
namespace: kube-system
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
This restricts matching pod-network traffic from application namespaces. It does not guarantee protection for host-network components or control-plane endpoints, and it does not restrict egress from system pods. Verify the effective paths in the cluster before relying on it.
Some CNI agents run as privileged pods in kube-system. A compromise or misconfiguration of the enforcement component can undermine policy guarantees. Restrict who can change CNI resources or deploy into the system namespace, and monitor the CNI agent as privileged infrastructure.
For monitoring, logging, or ingress namespaces, inventory dependencies before isolating them. Apply narrowly scoped policies to the selected workloads, then test scrape, control-plane, and node-originated paths rather than assuming a namespace-wide default deny is safe.
DNS Egress Rules
When egress is isolated, pods need an allowed path to their configured resolver. CoreDNS often runs in kube-system on port 53 over UDP and TCP, but NodeLocal DNSCache or other cluster layouts can change the destination. Confirm the resolver path before applying this example:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
# Allow DNS
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
# Allow other necessary egress (example)
- ports:
- protocol: TCP
port: 443
to:
- namespaceSelector: {}
The first rule targets pods selected in kube-system; confirm that this matches the cluster DNS path used by your CNI and service implementation. The second rule is intentionally broad: it permits TCP 443 to pods in every namespace, not arbitrary internet destinations. Standard NetworkPolicy has no FQDN selector; use specific ipBlock ranges or a CNI/cloud egress feature for external endpoints, and avoid stale or overly broad IP ranges.
CNI Providers and Policy Enforcement
Network Policy support varies by CNI provider. Not all providers implement all policy features:
| Implementation | Standard NetworkPolicy enforcement | Extensions and caveats |
|---|---|---|
| Calico | Supports standard ingress and egress policies when configured for policy enforcement | Calico-native policies add ordering, explicit actions, and other features; these are not part of the Kubernetes API. |
| Cilium | Supports standard ingress and egress policies | Cilium policy resources can add L7 rules; verify proxy and policy configuration for the deployed version. |
| Amazon VPC CNI for EKS | Supported in eligible configurations with VPC CNI 1.21+ | AWS documents platform constraints, including EC2 Linux nodes and kernel requirements; check the current EKS support matrix. |
| Flannel | Does not enforce NetworkPolicy by itself | Pair it with a policy-enforcing plugin if policies are required. |
| Weave Net | Historical implementation | The upstream repository was archived in June 2024; do not treat it as a current maintained choice. |
Calico NetworkPolicy
Calico extends standard NetworkPolicy with additional features:
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: api-isolation
namespace: production
spec:
selector: app == 'api-backend'
types:
- Ingress
- Egress
ingress:
- action: Allow
source:
selector: app == 'web-frontend'
destination:
ports:
- 8080
- action: Deny
egress:
- action: Allow
destination:
selector: app == 'postgres'
ports:
- 5432
- action: Allow
protocol: TCP
destination:
ports:
- 53
Calico’s explicit action: Deny rules make policy intent clearer.
Cilium NetworkPolicy
Cilium uses eBPF for enforcement and supports L7 policy extensions when their proxy configuration is enabled:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: api-backend
ingress:
- fromEndpoints:
- matchLabels:
app: web-frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
Cilium policy extensions can filter supported protocols such as HTTP, Kafka, and DNS at L7 when the required proxy and policy configuration are enabled.
Testing Network Policies
Verify your policies work correctly with connectivity tests:
Using kubectl to test connectivity
Use a test pod or exec into an existing workload to check a specific network path. A failed request does not identify the policy that blocked it; combine the probe with service health, DNS, endpoint, and CNI flow checks.
Test ingress by creating a busybox pod and reaching the target:
kubectl run --rm -i test-pod \
--image=busybox \
--restart=Never \
--command -- wget -q -O- http://api-backend:8080/health
A successful request shows that this path worked from the test pod. Ensure the test pod has the same namespace and labels as the source workload you intend to validate. A timeout alone does not prove a NetworkPolicy drop: the service may be unhealthy, the port may be wrong, or routing/DNS may be failing. Use a client that speaks the target protocol; for HTTPS, use curl with normal certificate validation when possible.
Test egress the other way around — exec into the source pod and reach outward:
kubectl exec -it api-backend-pod -- sh -c "nc -vz postgres 5432"
If nc is installed and the connection fails, check both the source egress and destination ingress policies, then inspect DNS, service endpoints, and CNI flow logs. A TCP probe confirms reachability only; use a PostgreSQL client such as pg_isready to check the database protocol.
Before assuming a policy is at fault when a test fails, check three things: the pod is actually running (kubectl get pods), the service name resolves (kubectl exec test-pod -- nslookup api-backend), and the port in your policy matches the service port. Port mismatches masquerade as policy failures more often than actual policy misconfigurations.
For repeated testing, keep a debug pod around instead of creating a new one every time:
kubectl run debug-pod --image=busybox --restart=Never -- sleep 3600
kubectl exec -it debug-pod -- sh
You get a persistent shell with the same network properties as your application pods, without the overhead of recreating a throwaway container each time.
Using a policy visualizer
Cilium Hubble and Calico’s policy tooling can help visualize flows or assess policy changes, depending on the product and edition. Treat visualizers as aids: verify the effective rules against live flow data and the CNI configuration, since a diagram alone does not prove enforcement.
You will notice the value when you have more than five policies across multiple namespaces. Reading YAML files, it is easy to miss an allow rule buried in a large policy. A picture shows you the whole topology at once: which pods reach which services, where default-deny is active, and where DNS exceptions exist. During security audits, this helps confirm no unexpected exposure paths exist between workloads.
If you do not have commercial tooling, kubectl get networkpolicy --all-namespaces lists all policies fast, and kubectl describe on individual policies shows the full rule set. Pair that with a topology diagram of your services and you can trace paths manually. Incident response is where this really pays off — you can quickly confirm whether a policy change caused an outage.
Production Failure Scenarios
Common Policy Failures
Policy Blocking All Traffic
A default-deny accidentally applied to the wrong namespace, or an overly broad rule that blocks your application’s actual dependencies. The result is sudden outage with no obvious cause in application logs.
Test in staging first. Apply to production during low-traffic windows. Have a rollback plan.
DNS Resolution Fails After Default Deny Egress
This is the most common mistake. Default deny goes in, DNS stops working, pods cannot resolve service names or reach each other.
DNS often uses port 53, but the resolver path varies by cluster. Identify and allow that path before applying default-deny egress to production workloads.
CNI Does Not Support Your Policy Features
Flannel alone does not enforce Kubernetes NetworkPolicy. Feature support in other implementations depends on plugin version and configuration, and vendor extensions are not interchangeable with the standard API. Weave Net’s upstream repository is archived, so do not choose it for a new production deployment. Check the current CNI documentation and test enforcement before relying on a feature.
Provider and Compliance Considerations
CNI Provider Trade-off Comparison
| Implementation | What to verify before choosing |
|---|---|
| Calico | Standard policy enforcement is available when configured; Calico-native policy features use a separate API. |
| Cilium | Standard policies are supported; L7 enforcement requires Cilium policy resources and the relevant proxy configuration. |
| Amazon VPC CNI for EKS | NetworkPolicy support requires supported plugin/platform versions and applies only to eligible node configurations. |
| Flannel | Does not enforce NetworkPolicy by itself. |
| Weave Net | Archived upstream project; unsuitable as a new maintained production dependency. |
Choose based on the cluster platform, required policy features, support lifecycle, and the team’s ability to operate the implementation. Validate the exact version and configuration rather than relying on a generic “full” or “partial” label.
Compliance Checklist
NetworkPolicies can contribute to workload segmentation controls. They do not, by themselves, establish compliance with PCI DSS, SOC 2, or another framework; map them to the controls in scope and retain evidence that the cluster enforces the intended rules:
# Default deny for PCI-DSS scoped namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: cardholder-data
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Key checklist items:
- Default deny applied to the in-scope workloads, with documented exceptions
- CNI policy enforcement confirmed for the cluster and node types in scope
- Resolver egress allowed on the ports and destinations used by the cluster
- Payment-data workloads isolated from unrelated workloads and external paths
- Policy, CNI, and cloud network changes recorded and reviewable
- Policy effectiveness reviewed on a defined schedule
Advanced L7 Policies
L7 Policy Examples with Cilium
Cilium supports HTTP-level network policies for fine-grained L7 control:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-access-control
namespace: production
spec:
endpointSelector:
matchLabels:
app: api-backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: "/api/v1/.*"
- method: POST
path: "/api/v1/users"
- method: GET
path: "/api/v1/health"
This policy allows the frontend to reach the API on port 8080 for the listed methods and paths. Requests that do not match those HTTP rules are not allowed by this policy.
# Allow GET requests to the API for a microservice
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: deny-admin-paths
namespace: production
spec:
endpointSelector:
matchLabels:
app: internal-api
ingress:
- fromEndpoints:
- matchLabels:
app: web-frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: "/api/v1/.*"
# Implicitly deny POST, PUT, DELETE, etc.
Common Pitfalls / Anti-Patterns
Blanket podSelector: {}
podSelector: {} selects every pod in the policy’s namespace. If that namespace contains system workloads, applying a restrictive policy to all of them can interrupt required traffic.
Always scope policies to your application pods specifically.
A restrictive policy can interrupt system dependencies such as cluster DNS or monitoring when those endpoints are selected by the policy. Components and resolver paths vary by cluster, so inventory the actual endpoints and check CNI flow data instead of assuming all system traffic follows the same pod-network path.
The dangerous pattern looks like this:
# Applies to system pods too
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
# Intended only for application pods
- from:
- podSelector:
matchLabels:
app: web-frontend
Scope the selector to your application workloads only. Use label prefixes like app:, component:, or tier: to separate system pods from application pods. If you need a default-deny in a namespace that also runs system components, apply the default-deny to application pods only and handle system namespace isolation separately with explicit allow rules for the system components. Run kubectl get pods --show-labels against the target namespace before applying any broad selector.
Ingress Only
Focusing only on ingress leaves egress wide open. A compromised pod can still exfiltrate data or call out to malicious servers.
Define both ingress and egress rules. Include DNS.
Ingress-only thinking is tempting because the attack surface feels obvious: someone calling your API, someone reaching your web server. But a pod with only ingress restrictions behaves like a server with an open outbound firewall. If an attacker compromises your API pod, they can use it as a launchpad — reach command-and-control servers, mine cryptocurrency on external infrastructure, scan internal services for further exploitation.
Egress filtering is where the real value of defense-in-depth lives. A compromised web-frontend pod should not reach your database directly. It reaches the API on the API’s ingress port. The database then has its own ingress rules. Even if one pod is compromised, lateral movement is cut off.
To implement proper egress control, start with default-deny egress on every namespace with untrusted or multi-tenant workloads. Then add explicit egress rules for DNS (port 53 to kube-system), external APIs your application calls, and the specific backend services each tier needs. Avoid allowing all egress to internal networks — scope it to the exact pods and ports each source requires.
Forgetting DNS Egress
Default deny plus no DNS rule means no service discovery works. Your pods cannot resolve .cluster.local addresses.
If the workload uses DNS, include its configured resolver path in the egress design and test name resolution before rollout.
When a pod resolves api-backend.production.svc.cluster.local, its query goes to the resolver configured for that pod. In many clusters this is CoreDNS in kube-system; in others, a node-local cache forwards the query. If egress policy blocks that path, service discovery and any health checks that resolve service names can fail.
When the resolver is a pod, a namespace selector is usually more stable than hardcoded CoreDNS pod IPs. Adapt the selector and ports to the actual resolver path; DNS uses both UDP and TCP port 53:
egress:
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
Include the resolver allowance in each policy that isolates egress; NetworkPolicies are namespaced, so a policy in one namespace does not automatically apply to workloads in another.
Observability Checklist
NetworkPolicy drops can look like application timeouts, so monitor the enforcement layer as well as the service:
- Confirm the installed CNI reports NetworkPolicy enforcement as healthy.
- Track denied and allowed flow counts by namespace, source, destination, and port where the CNI exposes them.
- Alert on sudden increases in denied traffic after policy changes, while avoiding alerts on expected probes.
- Check DNS success and application dependency health after enabling or changing egress rules.
- Keep policy changes and connectivity-test results tied to the same deployment or change record.
- Verify that monitoring and CNI control-plane traffic remains allowed after namespace isolation.
Quick Recap Checklist
- Confirm the cluster’s CNI plugin enforces Kubernetes Network Policies.
- Apply default-deny ingress and egress to the workloads that need isolation.
- Allow DNS over both UDP and TCP on port 53 before restricting egress.
- Check that pod and namespace selectors match the intended labels.
- Test both allowed and blocked connections in staging before rollout.
Interview Questions
Expected answer points:
- Where the network plugin provides the default Kubernetes pod network, pods can generally communicate unless traffic is segmented
- Network Policies implement microsegmentation by explicitly defining allowed ingress and egress traffic
- For an isolated direction, allowed traffic is the union of matching policies, subject to NetworkPolicy semantics and the plugin’s enforcement
- Policy is pod-scoped — each pod has its own ingress and egress rules that are additive when multiple policies select the same pod
- Enforces least-privilege: start with default deny, then explicitly allow only required traffic patterns
Expected answer points:
- Create a NetworkPolicy with empty podSelector (`{}`) to select all pods in the namespace
- Set policyTypes to include both Ingress and Egress for full denial
- Combined default deny: set policyTypes to ["Ingress", "Egress"] with no ingress/egress rules defined
- Apply default deny before deploying any application, then add allow policies as you deploy services
- When egress is isolated, allow the resolver path configured for the cluster before production rollout; it may use CoreDNS or a node-local DNS cache
Expected answer points:
- podSelector selects pods within the same namespace as the policy by label match
- namespaceSelector selects entire namespaces, often combined with a podSelector for fine-grained control
- Example: allow ingress from frontend namespace pods labeled app=web-frontend AND from monitoring namespace pods labeled app=prometheus
- namespaceSelector uses the `kubernetes.io/metadata.name` label to match namespace names
- Without namespaceSelector, podSelector only matches pods in the policy's own namespace
Expected answer points:
- Standard NetworkPolicy provides additive ingress and egress allow rules when the installed CNI enforces them
- Calico-native policy resources add ordering and explicit actions; Cilium policy resources can add L7 filtering
- Flannel does not enforce NetworkPolicy by itself, while AWS VPC CNI support depends on EKS version and node configuration
- Weave Net is an archived upstream project, not a current maintained choice
- Verify the exact plugin version, configuration, and supported feature set before relying on enforcement
Expected answer points:
- Network Policies are additive — the union of all allowed traffic is permitted when multiple policies select the same pod
- This means you must carefully design policies to avoid unintended exposure from combined rules
- Calico-native policy resources support an `order` field, but standard Kubernetes NetworkPolicy has no priority field
- For standard NetworkPolicy, there is no evaluation order to rely on; applicable allow rules combine additively
- Use a CNI-specific policy API only when you need explicit deny or ordered rules, and account for how it interacts with standard policies
Expected answer points:
- DNS commonly uses port 53 (TCP and UDP), but confirm the resolver pods or node-local DNS path in your cluster
- If DNS queries go directly to CoreDNS pods, an egress rule can select their namespace and allow TCP and UDP port 53; adapt it for NodeLocal DNSCache or another resolver path
- Include both UDP and TCP protocols since DNS can use either
- Without an appropriate DNS path, pods may fail to resolve service names or external domains
- When egress is isolated, allow the resolver path before applying the policy to production workloads
Expected answer points:
- Cilium supports HTTP-level filtering at L7, not just port and protocol at L3/L4
- L7 rules can restrict by HTTP method (GET, POST, PUT, DELETE) and URL path regex patterns
- Example: allow only frontend to reach the API on specific paths like /api/v1/.* but block /api/v1/admin
- CiliumNetworkPolicy uses toPorts instead of ports, with nested rules.http for L7 filtering
- Cilium also supports Kafka and DNS filtering at L7
- Standard K8s NetworkPolicies cannot filter by HTTP method or path — only by pod selector, namespace, and port
Expected answer points:
- Use kubectl exec or kubectl run to create a test pod and attempt connectivity to the target service
- A successful probe confirms that path worked; a timeout can also indicate DNS, service, routing, or application failure
- Test both allowed paths (should succeed) and blocked paths (should fail) for comprehensive coverage
- Use CNI flow visibility or policy tooling where available, and verify findings against live traffic and plugin configuration
- Calico Enterprise provides policy simulation and impact analysis tools
- Test DNS resolution separately to ensure the DNS egress rule works correctly
Expected answer points:
- Use NetworkPolicies as one segmentation control when they are in scope for the system and framework mapping
- Apply default-deny to the relevant namespace, then document and validate the required exceptions
- Retain policy change records and evidence from the CNI that enforcement is active
- Assess node, cloud network, ingress, egress, and external-service paths separately
- Have the compliance owner confirm the applicable control mapping; a policy manifest alone is not proof of compliance
Expected answer points:
- Applying default-deny to the wrong namespace — causes sudden outage with no obvious cause in application logs
- Forgetting DNS egress rule — pods cannot resolve service names after default deny egress is applied
- Only configuring ingress — egress remains wide open, allowing data exfiltration or calls to malicious servers
- Using overly broad podSelector: {} — selects system pods and breaks core functionality
- Assuming all CNI plugins support all policy features — Flannel does not support network policies at all
- Not testing in staging before production — policy errors only appear when traffic is blocked in production
Expected answer points:
- eBPF runs at the kernel level, allowing per-connection tracking and visibility without traversing iptables chains
- eBPF can enforce policies at the socket level, reducing latency compared to iptables-based policy enforcement
- eBPF provides richer observability — you can see per-connection statistics, latency histograms, and TCP state
- Cilium's L7 proxy integration with eBPF allows HTTP-aware policies without sidecar proxies
- Traditional iptables-based policies require conntrack and iptables rules for each policy, which scales poorly in large clusters
Expected answer points:
- Inventory which system endpoints are pod-networked, host-networked, or outside the cluster before writing a policy
- Standard NetworkPolicy behavior for hostNetwork pods is undefined; verify the CNI behavior and node/control-plane paths
- Apply tested, narrowly scoped rules and preserve required DNS, monitoring, and control-plane flows
- Use RBAC to limit who can change system namespace workloads or policy resources
- Do not assume a namespace-wide default deny protects every system endpoint
Expected answer points:
- Use namespaceSelector to allow traffic from specific namespaces, combined with podSelector for fine-grained control
- Example: ingress rule allows from namespace with label name=frontend and pods labeled app=web-frontend
- For DNS-based discovery, allow each source pod’s configured resolver path; it is commonly CoreDNS but can be node-local
- Calico's Tiered policies can enforce namespace-level isolation with hierarchical policy evaluation
- Be explicit — too many namespace selectors create complex policies that are hard to audit
Expected answer points:
- Network Policies control traffic flow at L3/L4 but do not restrict what a pod can do if it receives traffic
- Pod Security Admission enforces Pod Security Standards, while legacy PodSecurityPolicy was removed in Kubernetes 1.25
- Use both together: network policies block unauthorized traffic, pod security restricts pod capabilities
- Network policies are ineffective if a compromised pod can escalate privileges to run with host network or privileged access
- Use defense in depth with NetworkPolicies, RBAC, encryption for secrets at rest, and Pod Security Admission or another admission controller
Expected answer points:
- Start with default-deny ingress and egress for all three tiers
- Allow frontend to reach API on the API's ingress port (e.g., 8080)
- Allow API to reach database on the database's ingress port (e.g., 5432 for PostgreSQL)
- All three tiers need DNS egress to kube-system on port 53
- For external access, use an Ingress controller with its own policy allowing HTTP/HTTPS traffic to frontend only
- API backend may need egress to external APIs or cloud services — allow explicitly per requirement
- Database should have no ingress from external networks and minimal egress (only to API pods)
Expected answer points:
- Service mesh like Istio uses sidecar proxies (Envoy) to intercept all pod traffic at L7
- Network policies work at L3/L4 before traffic reaches the sidecar, providing foundational filtering
- Istio's AuthorizationPolicy provides L7-aware access control that works alongside NetworkPolicy
- If a pod has a sidecar, all traffic goes through the sidecar — network policy enforcement may appear to be bypassed but is actually applied before the sidecar intercepts
- Best practice: use NetworkPolicy for L3/L4 baseline (default deny, DNS, known services) and AuthorizationPolicy for L7 fine-grained control
Expected answer points:
- The policy is enforced immediately by the CNI plugin — existing connections may be affected if they are not among the allowed traffic
- Established connections that are now blocked will be terminated at the connection level (not immediately, but when they try to send)
- For rolling deployments, the policy is applied when the pod is recreated or when the CNI plugin reconciles
- To avoid disruption, apply policies during low-traffic windows and have a rollback plan
- Test in staging first — apply to production during maintenance windows when impact can be contained
Expected answer points:
- Check if the destination service's pod has a policy that blocks ingress from the source
- Check if the source service's pod has a policy that blocks egress to the destination
- Verify DNS resolution works: can the source resolve the destination service name? (DNS egress rule to kube-system on port 53)
- Verify the port numbers match in both the policy and the service definition
- Check if the pod selectors actually match the source and destination pods (label mismatch is common)
- Test with a debug pod using kubectl run --rm -it to manually verify connectivity and diagnose
Expected answer points:
- Standard NetworkPolicy behavior for hostNetwork pods is undefined; the most common CNI behavior treats their traffic like node-IP traffic, though some plugins can enforce policies for them
- Node-to-pod traffic is allowed by NetworkPolicy semantics in some cases, and actual behavior can depend on plugin and service implementation
- Use Pod Security Admission with an appropriate Pod Security Standard or a validating admission policy to restrict host networking; NodeRestriction is not that control
- For components that need host networking, assess node-level firewall or cloud security controls and test the cluster’s actual behavior
- Do not assume hostNetwork traffic is filtered or unfiltered without checking the CNI documentation
Expected answer points:
- Start with default-deny egress to block all outgoing traffic
- Explicitly allow required egress destinations, including the cluster’s configured DNS resolver path and known external services
- For databases, allow egress only to the specific application pods that need database access, not to all pods
- Consider egress gateways or NAT policies to restrict which pods can reach external networks
- Cilium policy extensions can filter supported L7 traffic when configured, but this is not a substitute for egress monitoring or controls on encrypted and unsupported protocols
- Monitor egress traffic patterns in production to understand normal behavior before tightening policies
Further Reading
- Kubernetes Network Policies documentation - Official docs covering policy syntax and examples
- Calico Network Policy - Extended policy features for Calico deployments
- Cilium Network Policy - L7 HTTP-aware policies and eBPF-based enforcement
- Kubernetes Pod Security Standards - Admission controls that complement network segmentation
- Amazon EKS Network Policies - Current AWS VPC CNI policy support and platform constraints
Conclusion
- Default deny policies applied to untrusted namespaces first
- Only required traffic explicitly allowed per application
- DNS egress allowed (port 53 to cluster DNS service)
- Policies tested in staging before production
- CNI plugin capabilities verified for required features
- Policy visualization used to check for unintended exposure
- Network Policies combined with RBAC and Secrets encryption
- Policy rationale documented and reviewed during security audits
For more on Kubernetes networking, see the Services and Networking post.
Category
Related Posts
Cloud Security: IAM, Network Isolation, and Encryption
Implement defense-in-depth security for cloud infrastructure—identity and access management, network isolation, encryption, and security monitoring.
Kubernetes Services and Ingress: Types and Routing
Master Kubernetes service types and Ingress controllers to expose your applications inside and outside the cluster with proper load balancing and routing.
Secrets Management: Vault, Kubernetes Secrets, and Env Vars
Learn how to securely manage secrets, API keys, and credentials across microservices using HashiCorp Vault, Kubernetes Secrets, and best practices.