How Do You Create a Default-Deny Network Policy?

intermediate|network policiesdevopssreplatform engineerCKACKAD
TL;DR

A default-deny Network Policy selects all Pods in a namespace (podSelector: {}) and specifies Ingress and/or Egress policy types with no allow rules. This blocks all traffic by default, forcing you to create explicit allow policies for legitimate communication.

Detailed Answer

A default-deny Network Policy is the foundation of secure Kubernetes networking. It blocks all traffic to and from Pods in a namespace, requiring explicit allow rules for every communication path.

Deny All Ingress

Block all incoming traffic to Pods in a namespace:

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

Deny All Egress

Block all outgoing traffic from Pods in a namespace:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress           # No egress rules = deny all outgoing traffic

Deny All Ingress and Egress

The most restrictive starting point — block everything:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

After Default-Deny: Allow DNS

Egress deny breaks DNS resolution, which breaks almost everything. Always allow DNS:

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

After Default-Deny: Allow Specific Traffic

With default-deny in place, add explicit allow rules for each communication path:

# Allow frontend to reach the API
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080
---
# Allow API to reach the database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-db
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: database
      ports:
        - protocol: TCP
          port: 5432

Complete Default-Deny Setup

A typical production namespace setup:

# 1. Deny all traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: 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
---
# 3. Allow ingress from the Ingress controller
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-controller
  namespace: production
spec:
  podSelector:
    matchLabels:
      exposed: "true"
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080

Testing Default-Deny

# Deploy a test Pod
kubectl run test-pod --image=busybox --rm -it -n production -- sh

# Test DNS (should work after DNS allow policy)
nslookup api-service

# Test connectivity to a Service (should fail without allow policy)
wget -qO- --timeout=5 http://api-service:8080
# wget: download timed out

# After adding allow policy, test again (should succeed)
wget -qO- --timeout=5 http://api-service:8080
# {"status": "ok"}

Common Mistake: Forgetting DNS

The most common issue after applying default-deny egress is that DNS stops working. Symptoms include:

  • Pods cannot resolve Service names
  • Applications fail with "name resolution failed" errors
  • Direct IP connections work but DNS-based connections fail

Always create the DNS allow policy immediately after the default-deny policy.

Why Interviewers Ask This

Interviewers ask this because default-deny is the foundation of zero-trust networking in Kubernetes. It is a CKA/CKAD exam topic and a production security requirement.

Common Follow-Up Questions

What happens to DNS after applying a default-deny egress policy?
DNS is blocked because egress to kube-dns on port 53 is denied. You must create an explicit egress rule allowing UDP port 53 to the kube-system namespace.
Do you need separate policies for ingress and egress?
You can combine both in one policy, but separate policies are often cleaner. A deny-all-ingress policy does not affect egress, and vice versa.
How do you test that default-deny is working?
Deploy a test Pod and attempt to curl a Service. Without an allow policy, the connection should time out. With an allow policy, it should succeed.

Key Takeaways

  • Default-deny is the starting point for Kubernetes network security — apply it to every namespace.
  • After applying default-deny, you must explicitly allow DNS, inter-service communication, and external access.
  • An empty podSelector ({}) targets all Pods in the namespace.

Related Questions

You Might Also Like