Skip to main content

Ensure CNI Supports Network Policies

More Info:

There are a variety of CNI plugins available for Kubernetes. If the CNI in use does not support Network Policies it may not be possible to effectively restrict traffic in the cluster.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. Identify the current CNI plugin

    • Run on: any machine with kubectl access
    • Command:
      kubectl get pods -n kube-system -o wide | grep -iE 'calico|cilium|weave|flannel|canal|antrea|ovn|aws-node|azure-cni|gke|kube-router'
      kubectl get daemonset -n kube-system
      kubectl get pods -n kube-system -o yaml > /tmp/kube-system-pods.yaml
    • Review: Look for CNI-related DaemonSets/Pods (e.g., calico-node, cilium, weave-net, aws-node, azure-cni, antrea, kube-router) to determine which CNI is installed.
  2. Determine whether the CNI supports NetworkPolicies

    • Using the CNI name from step 1, consult its official documentation to confirm:
      • Whether it supports Kubernetes networking.k8s.io/v1 NetworkPolicy.
      • Any feature flags or modes required to enable policy enforcement.
    • Evidence:
      • Save a short note in your ops documentation (e.g., /tmp/cni-networkpolicy-support.txt) recording:
        • CNI name and version (from Pod image tag).
        • URL or doc section stating NetworkPolicy support and any prerequisites.
  3. Verify NetworkPolicy CRD and usage in the cluster

    • Run on: any machine with kubectl access
    • Commands:
      kubectl api-resources | grep -i networkpolicy
      kubectl get networkpolicy --all-namespaces
    • Review:
      • Confirm networkpolicies (group networking.k8s.io) are available.
      • Note whether any NetworkPolicies are currently defined and in which namespaces.
  4. Test effective policy enforcement (non-disruptive check)

    • Run on: any machine with kubectl access
    • Apply a temporary restrictive policy in a non-critical namespace (e.g., default), then test connectivity:
      # Create a simple deny-all-ingress policy
      cat <<'EOF' | kubectl apply -f -
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
      name: deny-all-ingress
      namespace: default
      spec:
      podSelector: {}
      policyTypes:
      - Ingress
      EOF

      # Launch two test pods
      kubectl run np-test-server -n default --image=nginx:1.25 --port=80 --restart=Never
      kubectl run np-test-client -n default --image=busybox:1.36 --restart=Never -- sleep 3600

      # After pods are Running, try to connect
      SERVER_IP=$(kubectl get pod np-test-server -n default -o jsonpath='{.status.podIP}')
      kubectl exec -n default np-test-client -- wget -qO- "http://$SERVER_IP:80" || echo "connect failed"
    • Review:
      • If the CNI enforces policies, the wget should fail (connection refused/timed out).
      • If the wget succeeds, NetworkPolicies are not being enforced by the current CNI or are misconfigured.
    • Cleanup:
      kubectl delete networkpolicy deny-all-ingress -n default
      kubectl delete pod np-test-server np-test-client -n default --ignore-not-found
  5. Decide remediation approach if NetworkPolicies are unsupported or ineffective

    • If documentation or the test indicates no or ineffective NetworkPolicy support:
      • Decide whether to:
        • Migrate to a CNI that supports NetworkPolicies (e.g., Calico, Cilium, Antrea, etc.), or
        • Implement an alternate traffic restriction mechanism (e.g., cloud-provider security groups, service mesh policy, host firewall rules).
    • Gather current state to inform migration planning:
      kubectl get nodes -o wide
      kubectl get pods -A -o wide
      kubectl cluster-info dump > /tmp/cluster-info-dump.json
    • Plan for:
      • Maintenance window and rollback.
      • CNI-specific migration guide (from vendor docs).
      • Impact on existing Pods and IP addressing.
  6. Verify post-remediation behavior

    • After migrating CNI or enabling NetworkPolicy features, repeat step 4’s test exactly:
      # Re-apply the deny-all policy and test connectivity as in step 4
      # (re-run the same commands)
    • Confirm:
      • NetworkPolicy API is present (step 3).
      • Connectivity test fails as expected under a deny-all policy.
    • Record results and updated CNI details in your cluster security documentation as proof of remediation.
Using kubectl
# 1) Identify the current cluster network plugin
# Run on: any machine with kubectl access
kubectl -n kube-system get pods -o wide \
-l k8s-app=calico-node,app=calico,app=flannel,k8s-app=flannel, \
k8s-app=weave-net,app=weave-net,k8s-app=cilium,app=cilium, \
app=canal,k8s-app=canal,app=kube-router,k8s-app=kube-router

# If nothing obvious is returned, list all Pods in kube-system for manual inspection:
kubectl -n kube-system get pods -o wide

Problem indication:
You cannot clearly identify a well-known CNI plugin from kube-system (e.g., no Calico, Cilium, Weave Net, Antrea, Kube-router, Canal, etc.), or you see a vendor-specific/“custom” CNI that must be checked against its documentation to confirm NetworkPolicy support.


# 2) Check if any NetworkPolicy objects exist in the cluster
# Run on: any machine with kubectl access
kubectl get networkpolicies --all-namespaces

Problem indication:

  • Output is: No resources found for all namespaces, and you believe there should be traffic restrictions; or
  • Only a few test namespaces use NetworkPolicies while critical namespaces (e.g., default, kube-system, app namespaces) have none, suggesting network policy is not actually used for isolation.

# 3) Test basic NetworkPolicy behavior in a non-critical namespace
# (this does NOT enforce policy cluster‑wide; it is a safe, temporary check)
# Run on: any machine with kubectl access

# 3.1 Create an isolated test namespace
kubectl create namespace netpol-test

# 3.2 Create two test pods: one "backend" (with a label) and one "client"
kubectl -n netpol-test apply -f - << 'EOF'
apiVersion: v1
kind: Pod
metadata:
name: backend
labels:
app: backend
spec:
containers:
- name: backend
image: docker.io/library/nginx:stable
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: client
spec:
containers:
- name: client
image: docker.io/library/busybox:1.36
command: ["sh", "-c", "sleep 3600"]
EOF

# Wait for pods to be Ready
kubectl -n netpol-test wait --for=condition=Ready pod/backend --timeout=120s
kubectl -n netpol-test wait --for=condition=Ready pod/client --timeout=120s

# Get backend Pod IP
BACKEND_IP=$(kubectl -n netpol-test get pod backend -o jsonpath='{.status.podIP}')
echo "$BACKEND_IP"

# 3.3 Confirm client can reach backend BEFORE any NetworkPolicy
kubectl -n netpol-test exec client -- wget -qO- "http://$BACKEND_IP" || echo "connect failed"

# 3.4 Apply a DENY-ALL ingress NetworkPolicy to backend
kubectl -n netpol-test apply -f - << 'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-backend-ingress
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress: []
EOF

# 3.5 Test again AFTER applying NetworkPolicy
kubectl -n netpol-test exec client -- wget -qO- "http://$BACKEND_IP" || echo "connect failed"

Problem indication (NetworkPolicy behavior test):

  • If the request from client to backend still succeeds after the deny-backend-ingress policy is applied (i.e., wget returns the Nginx HTML response instead of failing/timeouts), this strongly suggests:
    • The current CNI does not implement Kubernetes NetworkPolicy, or
    • It is misconfigured/disabled.

If wget fails/blocks after the policy is applied, the CNI is at least enforcing basic ingress NetworkPolicies.


# 4) Cleanup test resources after review
# Run on: any machine with kubectl access
kubectl delete namespace netpol-test

You must now manually decide, based on:

  • Which CNI you discovered in step 1 and its documentation, and
  • Whether NetworkPolicy behavior in step 3 matched expectations,

whether to keep the current CNI, replace it with one that supports NetworkPolicies, or implement an alternate traffic restriction mechanism. kubectl cannot make that decision automatically.

Automation
#!/usr/bin/env bash
# Purpose: Report whether the current CNI appears to support Kubernetes NetworkPolicies

set -euo pipefail

echo "=== 1) Detect CNI plugin(s) in use (from node annotations) ==="
kubectl get nodes -o custom-columns=NAME:.metadata.name,CNI:.metadata.annotations['k8s\.ovn\.org/node-chassis-id','vpc\.amazonaws\.com/eni-id','cni\.projectcalico\.org/IPv4IPIPTunnelAddr','weave\.works/agent','flannel\.alpha\.coreos\.com/backend-type'] --no-headers || true

echo
echo "=== 2) List CNI-related DaemonSets in kube-system (common CNIs) ==="
kubectl get ds -n kube-system \
-o custom-columns=NAME:.metadata.name,IMAGES:.spec.template.spec.containers[*].image \
--no-headers | grep -Ei 'calico|cilium|weave|flannel|canal|cni|ovn|antrea|multus' || \
echo "No common CNI DaemonSets detected in kube-system."

echo
echo "=== 3) List CNI directories on each node (from Node status) ==="
kubectl get nodes -o custom-columns=NAME:.metadata.name,CNI_DIRS:.status.nodeInfo.containerRuntimeVersion --no-headers

echo
echo "=== 4) Try creating a test NetworkPolicy (non-disruptive) ==="
TEST_NS="cni-networkpolicy-check"
TEST_NP="np-cni-support-check"

# Create an isolated namespace for the test policy
kubectl get ns "${TEST_NS}" >/dev/null 2>&1 || kubectl create ns "${TEST_NS}" >/dev/null

# Apply a minimal NetworkPolicy that selects no pods (no traffic impact)
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: np-cni-support-check
namespace: cni-networkpolicy-check
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
EOF

echo
echo "NetworkPolicy object created. Current status:"
kubectl get networkpolicy -n "${TEST_NS}" "${TEST_NP}" -o yaml | sed -n '1,40p'

echo
echo "=== 5) Summary guidance ==="
cat <<'EOM'
Review results:

1) CNI plugin identification:
- From step (1) and (2), identify which CNI you are running (e.g. calico, cilium, antrea, weave, flannel, canal, ovn-kubernetes, etc.).
- Cross-check that plugin's documentation to confirm it supports Kubernetes NetworkPolicies.

2) NetworkPolicy object behavior:
- If the NetworkPolicy resource in step (4) was ACCEPTED by the API server (it appears in kubectl get output),
that only confirms the API supports NetworkPolicies, NOT that the CNI enforces them.
- You must still verify in a follow-up manual test that traffic is actually restricted when NetworkPolicies are applied.

Indicators of a potential problem:
- Step (2) does not show a known NetworkPolicy-capable CNI (e.g. only "flannel" with no policy controller).
- You are using a custom or cloud-vendor CNI whose docs do not explicitly state NetworkPolicy support.
- NetworkPolicy objects exist but connectivity tests show they are not enforced.

This script cannot determine with certainty that your CNI enforces NetworkPolicies; it only surfaces
the likely CNI implementation and confirms that NetworkPolicy objects can be created for manual review.
EOM

Additional Reading: