Kubernetes Network Policy is failing to block traffic because the CNI plugin managing network rules doesn’t support or enforce the policy correctly.

Common Causes and Fixes:

  1. CNI Plugin Doesn’t Support NetworkPolicy:

    • Diagnosis: Check your cluster’s CNI plugin. Many basic CNIs (like bridge or older flannel versions without vxlan or host-gw backend) do not implement Kubernetes NetworkPolicy resources.
      kubectl get ds -n kube-system
      # Look for your CNI daemonset, e.g., calico-node, flannel, cilium, weave-net
      
      If you see flannel with a configuration that doesn’t explicitly mention vxlan or host-gw, or a custom CNI that isn’t listed as supporting NetworkPolicy, this is your problem.
    • Fix: Replace your CNI with one that fully supports NetworkPolicy. Popular choices include Calico, Cilium, or Weave Net. For example, to switch to Calico:
      1. Uninstall your current CNI.
      2. Install Calico. The official installation manifest is usually found on their GitHub releases page.
      # Example for a typical Calico install (check official docs for latest version)
      kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
      
    • Why it works: Network policies are implemented by the CNI plugin. If the plugin isn’t designed to understand and enforce these policies, they will have no effect. A compliant CNI translates NetworkPolicy objects into actual firewall rules (like iptables or eBPF) on each node.
  2. NetworkPolicy is Applied to the Wrong Namespace:

    • Diagnosis: Network policies are namespace-scoped. A policy defined in namespace-a only affects pods within namespace-a. If your pods are in default and your policy is in kube-system, it won’t do anything.
      # Check the namespace of your policy
      kubectl get networkpolicy <policy-name> -n <policy-namespace> -o yaml
      
      # Check the namespace of the pods you expect to be affected
      kubectl get pods --all-namespaces
      
    • Fix: Ensure the metadata.namespace field in your NetworkPolicy definition matches the namespace of the pods you intend to control.
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: deny-all-ingress
        namespace: my-app-namespace # <-- Ensure this is correct
      spec:
        podSelector:
          matchLabels:
            app: my-app
        policyTypes:
        - Ingress
        ingress: [] # Empty ingress means deny all ingress by default
      
    • Why it works: Kubernetes resources, including NetworkPolicies, are namespaced. The control plane and the CNI plugin look for policies within the same namespace as the target pods to apply the rules.
  3. podSelector is Too Broad or Incorrect:

    • Diagnosis: The podSelector in a NetworkPolicy determines which pods the policy applies to. If it’s empty ({}) or matches all pods, the policy applies to all pods in the namespace. If it doesn’t match the labels of the pods you want to restrict, the policy won’t affect them.
      # Policy applying to all pods in 'my-app-namespace'
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: restrict-external
        namespace: my-app-namespace
      spec:
        podSelector: {} # <-- This matches ALL pods in the namespace
        ingress:
        - from:
          - podSelector:
              matchLabels:
                app: backend # Only allows traffic from pods with label app=backend
      
      To check the labels of your pods:
      kubectl get pods -n <your-namespace> --show-labels
      
    • Fix: Adjust the podSelector to accurately target the pods that should be subject to the policy.
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: restrict-external
        namespace: my-app-namespace
      spec:
        podSelector:
          matchLabels:
            app: frontend # <-- This policy now ONLY applies to pods with label app=frontend
        ingress:
        - from:
          - podSelector:
              matchLabels:
                app: backend
      
    • Why it works: The podSelector acts as a filter. The CNI plugin uses these labels to identify the specific network interfaces and endpoints on which to enforce the defined ingress and egress rules.
  4. Missing policyTypes or Incorrect policyTypes:

    • Diagnosis: A NetworkPolicy can specify ingress, egress, or both. If policyTypes is omitted, it defaults to Ingress if ingress rules are present, and Egress if egress rules are present. If you intend to block all ingress traffic, you must explicitly define policyTypes: ["Ingress"] and an empty ingress: [] section. If you want to block all egress, you need policyTypes: ["Egress"] and an empty egress: [] section.
      # This policy *allows* all ingress by default if policyTypes is omitted or only egress is specified
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: allow-all-ingress-by-mistake
        namespace: my-app-namespace
      spec:
        podSelector:
          matchLabels:
            app: my-app
        # policyTypes is missing, and ingress is defined, so it defaults to Ingress only
        ingress:
        - from: [] # Empty from *with* ingress defined means "allow all ingress"
      
    • Fix: Explicitly set policyTypes to include what you want to control. To block all ingress, use policyTypes: ["Ingress"] and an empty ingress: [] list. To block all egress, use policyTypes: ["Egress"] and an empty egress: [] list.
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: deny-all-ingress
        namespace: my-app-namespace
      spec:
        podSelector:
          matchLabels:
            app: my-app
        policyTypes:
        - Ingress # <-- Explicitly state we are controlling ingress
        ingress: [] # <-- An empty ingress rule denies all ingress traffic
      
    • Why it works: The policyTypes field tells the CNI which direction of traffic (ingress, egress, or both) the policy rules apply to. Without it, or with an incorrect setting, the CNI might not apply rules as expected, or might default to an "allow all" behavior for unspecified types.
  5. Traffic is Not Pod-to-Pod:

    • Diagnosis: NetworkPolicy primarily controls traffic between pods within the cluster. Traffic originating from outside the cluster (e.g., via a LoadBalancer Service, NodePort, or directly to a node’s IP) or traffic to external services is not affected by default. If your traffic is coming from a LoadBalancer, your NetworkPolicy won’t block it unless you have specific CNI features or additional configurations.
      # Check the type of the service exposing your application
      kubectl get svc <your-service-name> -n <your-namespace> -o yaml
      # Look for 'type: LoadBalancer' or 'type: NodePort'
      
    • Fix: For external traffic, you need to configure your Ingress controller or Service to restrict access. For traffic to external endpoints, you need to define egress rules with to selectors that explicitly allow connections to external IPs or CIDRs, or use a default-deny egress policy and then allow only necessary outbound connections.
      # Example: Allow egress to specific external CIDRs
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: allow-external-dns
        namespace: my-app-namespace
      spec:
        podSelector:
          matchLabels:
            app: my-app
        policyTypes:
        - Egress
        egress:
        - to:
          - ipBlock:
              cidr: 8.8.8.8/32 # Allow egress to Google DNS
          ports:
          - protocol: UDP
            port: 53
          - to:
          - ipBlock:
              cidr: 1.1.1.1/32 # Allow egress to Cloudflare DNS
          ports:
          - protocol: UDP
            port: 53
      
    • Why it works: NetworkPolicy operates at the pod network layer. External traffic bypasses this layer initially. Ingress controllers and LoadBalancer implementations handle traffic before it reaches the pod network, and external network routing handles egress traffic.
  6. CNI Firewall Rules Not Applying (iptables/eBPF issues):

    • Diagnosis: In some cases, the CNI might be configured correctly, but the underlying firewall rules (often iptables for older CNIs or eBPF for newer ones) are not being applied or are being overwritten. This can happen if other components on the node are also manipulating iptables or if the CNI agent has crashed or is misconfigured.
      # For iptables-based CNIs (e.g., older flannel, weave)
      # Check the KUBE-NETWORK-POLICY chain in the filter table
      sudo iptables -t filter -L KUBE-NETWORK-POLICY -n
      
      # For eBPF-based CNIs (e.g., Cilium, Calico with eBPF mode)
      # This is more complex and CNI-specific. For Cilium, you might check:
      cilium status # Check Cilium agent health
      # And potentially examine eBPF maps if you're deep in troubleshooting
      
      If you see no rules, or rules that don’t correspond to your NetworkPolicy definitions, this is the issue.
    • Fix: Restart the CNI daemonset pods on the affected nodes. If the problem persists, consider reinstalling the CNI or debugging the CNI agent logs.
      # Find the CNI daemonset pods
      kubectl get pods -n kube-system -l <cni-label-selector>
      
      # Delete them to force a restart (the daemonset will recreate them)
      kubectl delete pod <cni-pod-name> -n kube-system
      
    • Why it works: The CNI agent on each node is responsible for programmatically adding and managing the firewall rules that enforce NetworkPolicy. A restart ensures it re-evaluates its state and applies the correct rules based on the cluster’s NetworkPolicy objects.

You’ll next run into issues with NetworkPolicy affecting DNS resolution if you’re too restrictive with egress.

Want structured learning?

Take the full Kubernetes course →