πŸ“šBook Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) β€” free book giveaway!RSVP Booking.com Event
Security intermediate ⏱ 15 minutes K8s 1.28+

Default Deny NetworkPolicy: Zero-Trust Examples

Implement default deny network policies in Kubernetes for zero-trust pod networking. Block all ingress and egress by default, then allow only required traffic

By Luca Berton β€’ β€’ πŸ“– 5 min read

πŸ’‘ Quick Answer: Apply a default deny NetworkPolicy to block all ingress and egress traffic in a namespace, then create allow policies for specific required flows. Without any NetworkPolicy, all pods can communicate freely. A single empty-selector {} policy with no ingress/egress rules denies all traffic matching that direction.

The Problem

  • By default, all pods can communicate with all other pods (flat network)
  • A compromised pod can reach any service in the cluster
  • Compliance requires network segmentation and least-privilege communication
  • Need to prevent lateral movement after initial compromise
  • Must allow only explicitly required traffic paths

The Solution

Default Deny All Ingress

# Block all incoming traffic to pods in this namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}    # Applies to ALL pods in namespace
  policyTypes:
    - Ingress
  # No ingress rules = deny all incoming

Default Deny All Egress

# Block all outgoing traffic from pods in this namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  # No egress rules = deny all outgoing (including DNS!)

Default Deny Both (Full Isolation)

# Block all traffic in both directions
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Allow DNS (Required with Egress Deny)

# Must allow DNS or pods can't resolve service names
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

Allow Specific Service Communication

# Allow frontend β†’ api-server on port 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080
---
# Allow api-server β†’ database on port 5432
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-db
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api-server
      ports:
        - protocol: TCP
          port: 5432

Allow Ingress from External (Ingress Controller)

# Allow traffic from ingress-nginx namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-controller
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: frontend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080

Complete Zero-Trust Example

# 1. Default deny all
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]
---
# 2. Allow DNS for all pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}
  policyTypes: [Egress]
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - {protocol: UDP, port: 53}
        - {protocol: TCP, port: 53}
---
# 3. Allow ingress β†’ frontend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-external-to-frontend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: frontend
  policyTypes: [Ingress]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - {protocol: TCP, port: 8080}
---
# 4. Allow frontend β†’ api (ingress + egress)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: frontend-to-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: frontend
  policyTypes: [Egress]
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: api-server
      ports:
        - {protocol: TCP, port: 8080}

Common Issues

Pods can’t resolve DNS after default-deny-egress

  • Cause: DNS (port 53) is blocked by egress deny
  • Fix: Add explicit allow-dns NetworkPolicy (shown above)

NetworkPolicy has no effect

  • Cause: CNI doesn’t support NetworkPolicy (e.g., Flannel without policy engine)
  • Fix: Use Calico, Cilium, or Antrea β€” they enforce NetworkPolicy rules

Pods in same namespace can still communicate after deny

  • Cause: Another NetworkPolicy in the namespace allows the traffic
  • Fix: NetworkPolicies are additive β€” any allow rule permits traffic. Check all policies

Health checks failing after deny

  • Cause: Kubelet health probes come from node IP (not a pod)
  • Fix: Allow ingress from node CIDR; or use ipBlock with node subnet

Best Practices

  1. Default deny first, then allow β€” zero-trust approach
  2. Always allow DNS with egress deny β€” pods need name resolution
  3. Use namespace labels for cross-namespace rules β€” cleaner than IP blocks
  4. Test with kubectl exec + curl β€” verify connectivity after applying policies
  5. Label pods consistently β€” NetworkPolicy selectors depend on labels
  6. Document allowed flows β€” maintain a traffic matrix for the namespace
  7. Use CNI that supports NetworkPolicy β€” Calico, Cilium, Antrea (not basic Flannel)

Key Takeaways

  • Without NetworkPolicy, all pods can talk to all pods (flat network)
  • podSelector: {} + no rules = deny all (for that policyType)
  • NetworkPolicies are additive β€” if ANY policy allows traffic, it’s allowed
  • Default deny egress blocks DNS β€” always add an allow-dns policy
  • Required CNI support: Calico, Cilium, Antrea (basic Flannel doesn’t enforce)
  • Combine namespace selectors + pod selectors for precise traffic control
  • Zero-trust pattern: deny all β†’ allow DNS β†’ allow specific flows only
#networkpolicy #security #zero-trust #networking #isolation
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens