Advanced NetworkPolicy Patterns
The previous lessons showed you how to write individual rules for specific Pods. But in a real namespace with many Pods and multiple teams contributing policies, you need a broader strategy. What do you do with Pods that have no policy yet? How do you coordinate policies from different teams without conflicts? How do you target Pods that must come from a specific namespace and have specific labels at the same time?
These are the questions this lesson answers.
Default Deny-All as a Security Foundation
The deny-all pattern is a single policy that locks down an entire namespace in one shot. It uses an empty podSelector to match every Pod, and lists both Ingress and Egress in policyTypes with no rules. The result is that all inbound and outbound traffic across every Pod in the namespace is blocked.
nano deny-all.yamlapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: deny-all namespace: defaultspec: podSelector: {} policyTypes: - Ingress - Egresskubectl apply -f deny-all.yamlAfter applying this, every Pod in the namespace is isolated. No communication is possible, including DNS. From this clean baseline, you selectively open what you actually need by adding targeted policies. Nothing is accidentally allowed. Every permitted connection exists because someone wrote a rule for it.
kubectl get networkpolicyVerify the deny-all policy appears. Now add the selective allow for api to reach db (with DNS):
You apply the deny-all policy above. A Pod with no other NetworkPolicy tries to reach another Pod. What happens?
- Traffic is allowed because the Pod has no specific policy targeting it
- Traffic is blocked because the deny-all policy covers every Pod via
podSelector: {} - Only egress is blocked; ingress still works
Reveal answer
Traffic is blocked in both directions - the empty podSelector: {} matches every Pod in the namespace, and listing both Ingress and Egress with no rules blocks everything.
Policy Composition: Multiple Policies Are Additive
Once you have a deny-all in place, you start building your allowed connections one policy at a time. Each policy you add opens a specific communication path. Because policies are additive, they stack cleanly.
Say the db Pod needs to accept traffic from two separate sources: api and a monitoring agent in a different namespace. You could write a single policy with both rules. But you can also write two separate policies, one from each team:
# Policy from the app teamapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-api-to-db namespace: defaultspec: podSelector: matchLabels: app: db policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: api ports: - protocol: TCP port: 5432 # illustrative only# Policy from the monitoring teamapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-monitoring-to-db namespace: defaultspec: podSelector: matchLabels: app: db policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: monitoring ports: - protocol: TCP port: 9187 # illustrative onlyBoth policies select db. The result is that db accepts traffic from api on port 5432 AND from any Pod in the monitoring namespace on port 9187. Neither policy cancels the other.
NetworkPolicies are additive, not restrictive. Adding a new policy to a Pod can only increase what is permitted, never reduce it. If you want to tighten access, you must remove or modify existing policies, not add new ones. This is why starting from deny-all and building upward is the safer model.
Pod db has two NetworkPolicies: one allows ingress from app=api, another allows ingress from app=monitoring. A request arrives from a Pod labeled app=monitoring. Is it allowed?
- No, only the most recently applied policy counts
- Yes, because at least one policy permits it and policies are additive
- Only if both policies agree on the same source
Reveal answer
Yes - if any applicable policy permits the traffic, it is allowed. The second policy is sufficient on its own to grant access.
Cross-Namespace AND Logic
Namespaces and Pod labels can be combined in the same from entry to require both conditions simultaneously. This is the cross-namespace AND pattern: the source Pod must have the right label AND come from the right namespace.
nano cross-ns-policy.yamlapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-api-from-backend-ns namespace: defaultspec: podSelector: matchLabels: app: db policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: api namespaceSelector: matchLabels: team: backend # illustrative onlyBoth podSelector and namespaceSelector are inside the same - list item. The source must satisfy both. A Pod labeled app: api in a namespace not labeled team: backend is blocked. A Pod in a team: backend namespace without the app: api label is also blocked.
kubectl apply -f cross-ns-policy.yamlkubectl describe networkpolicy allow-api-from-backend-nsYou need to allow ingress from Pods labeled role=scraper that are also in a namespace labeled env=prod. What is the correct structure in the from field?
- Two separate
-entries, one forpodSelectorand one fornamespaceSelector - Both
podSelectorandnamespaceSelectorinside the same-entry
Reveal answer
Both inside the same - entry - this produces AND logic. Two separate entries would produce OR logic, allowing either condition independently.
Ports, Protocols, and Port Ranges
Each rule in ingress or egress can specify ports by number and protocol. Both TCP and UDP are supported. Some CNI plugins also support named ports (matching the port name defined in the Pod spec), and port ranges using endPort:
ingress: - from: - podSelector: matchLabels: app: api ports: - protocol: TCP port: 8000 endPort: 8080 # illustrative onlyThis allows TCP traffic on any port from 8000 to 8080 inclusive. The endPort field is only valid when the CNI plugin supports it. Without endPort, you list each port individually.
Putting It All Together
The recommended pattern for any production namespace is:
- Apply a deny-all policy first to lock everything down.
- Add targeted ingress allow policies for each service that needs to accept traffic.
- Add targeted egress allow policies for each service that needs to reach other services, including the DNS exception.
- Use cross-namespace AND selectors when the source must come from a specific namespace with specific labels.
- Rely on additive composition: each team owns the policies relevant to their services.
NetworkPolicies are your primary tool for network-level least privilege in Kubernetes. The deny-all baseline combined with selective allow policies gives you a clear, auditable record of every permitted connection in the namespace.