Skip to content

Kubernetes NetworkPolicy Reference: Default Deny, Egress, CIDR Rules & Three-Tier Patterns

Kubernetes NetworkPolicy is the built-in way to control pod-to-pod traffic. By default, all pods can talk to all other pods. A NetworkPolicy changes that — once you apply one to a pod, it becomes deny-all except what you explicitly allow. Requires a CNI plugin that supports NetworkPolicy (Calico, Cilium, Weave — not Flannel by default).

⚠️ NetworkPolicy requires a compatible CNI (Calico, Cilium, Canal, Weave). Flannel and many managed cluster defaults do NOT enforce policies. Verify with: kubectl get pods -n kube-system | grep -E "calico|cilium|weave"

1. Default Deny & Basic Allow

Default deny-all pattern and allowing specific ingress/egress
# Default deny-all ingress for a namespace (apply this first):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}             # applies to ALL pods in namespace
  policyTypes:
    - Ingress                 # only ingress rules (egress still allowed)

# Default deny-all (both ingress and egress):
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

# Allow ingress to the API pods from the frontend pods only:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api               # policy applies to pods with app=api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend  # allow from pods with app=frontend
      ports:
        - protocol: TCP
          port: 8080

# Multiple policies are additive (OR logic):
# Pod with two NetworkPolicies = union of all allowed traffic

2. Cross-Namespace Traffic

Allow traffic from pods in other namespaces using namespaceSelector
# Allow ingress from a specific namespace (e.g. monitoring namespace):
spec:
  podSelector:
    matchLabels:
      app: api
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring  # namespace label
      ports:
        - port: 9090   # Prometheus scrape port

# Allow from specific pods in specific namespace (AND logic — same from[] entry):
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            env: production
        podSelector:                    # AND: namespace AND pod label
          matchLabels:
            role: prometheus

# IMPORTANT: namespaceSelector + podSelector in the SAME from[] item = AND
# namespaceSelector + podSelector in SEPARATE from[] items = OR
ingress:
  - from:
      - namespaceSelector:             # item 1: any pod in monitoring namespace
          matchLabels:
            kubernetes.io/metadata.name: monitoring
      - podSelector:                   # item 2: OR any pod with role=admin (ANY namespace)
          matchLabels:
            role: admin

# Label namespaces for NetworkPolicy selectors:
# kubectl label namespace monitoring kubernetes.io/metadata.name=monitoring
# (kubernetes.io/metadata.name is auto-set in K8s 1.21+ for new namespaces)

3. Egress Rules

Control outbound traffic — allow DNS, external APIs, and database access
# Allow egress only to the database and DNS (deny all other outbound):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-egress
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Egress
  egress:
    # Allow DNS (ALWAYS include this or pod DNS breaks):
    - ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
    # Allow access to database pods:
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - port: 5432
    # Allow access to external HTTPS API:
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 10.0.0.0/8      # block internal ranges
              - 172.16.0.0/12
              - 192.168.0.0/16
      ports:
        - port: 443

# CRITICAL: If you add an Egress policyType without egress rules,
# ALL egress is denied — including DNS. Always explicitly allow UDP/TCP port 53.

4. CIDR-Based Rules & Node Access

Allow/deny traffic to external IP ranges, node IPs, and cloud load balancers
# Allow ingress from a specific CIDR (e.g. VPN range, office IP):
ingress:
  - from:
      - ipBlock:
          cidr: 10.100.0.0/24    # allow from VPN subnet
    ports:
      - port: 443

# Block access from a broad range except a subset:
ingress:
  - from:
      - ipBlock:
          cidr: 10.0.0.0/8
          except:
            - 10.50.0.0/16       # deny this specific subnet within the /8

# Allow access to cloud metadata API (sometimes needed):
egress:
  - to:
      - ipBlock:
          cidr: 169.254.169.254/32    # AWS/GCP/Azure instance metadata IP
    ports:
      - port: 80

# Allow egress to Kubernetes API server (for service accounts):
egress:
  - ports:
      - port: 443
    to:
      - ipBlock:
          cidr: <API-SERVER-IP>/32

# Verify NetworkPolicy is being enforced:
kubectl exec -it api-pod -- nc -zv postgres-service 5432   # should succeed
kubectl exec -it api-pod -- nc -zv other-service 8080       # should fail if blocked

5. Real-World Patterns

Three-tier (frontend/API/DB), monitoring access, and namespace isolation patterns
# Three-tier isolation (frontend -> api -> db):

# Tier 1: Frontend — allow ingress from ingress controller only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: frontend-ingress, namespace: production}
spec:
  podSelector: {matchLabels: {tier: frontend}}
  policyTypes: [Ingress]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx

# Tier 2: API — allow ingress from frontend only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: api-ingress, namespace: production}
spec:
  podSelector: {matchLabels: {tier: api}}
  policyTypes: [Ingress]
  ingress:
    - from:
        - podSelector: {matchLabels: {tier: frontend}}
      ports: [{port: 8080}]

# Tier 3: Database — allow ingress from API only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: db-ingress, namespace: production}
spec:
  podSelector: {matchLabels: {tier: database}}
  policyTypes: [Ingress]
  ingress:
    - from:
        - podSelector: {matchLabels: {tier: api}}
      ports: [{port: 5432}]

# Allow Prometheus to scrape all pods in namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: allow-prometheus-scrape, namespace: production}
spec:
  podSelector: {}                     # all pods
  policyTypes: [Ingress]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports: [{port: 9090}, {port: 8080}, {port: 3000}]

# Debug: list all NetworkPolicies and what they select:
kubectl get networkpolicy -A
kubectl describe networkpolicy -n production

Track Kubernetes releases at ReleaseRun. Related: Kubernetes RBAC Reference | Cilium Reference | Kubernetes Networking Reference

🔍 Free tool: K8s YAML Security Linter — after writing NetworkPolicies, check all related workload manifests for security misconfigurations.

Founded

2023 in London, UK

Contact

hello@releaserun.com