Ensure The CNI In Use Supports NetworkPolicies
More Info:
Without a CNI plugin that supports NetworkPolicies, traffic between pods cannot be restricted. Use a plugin that enforces network policy.
Risk Level
High
Address
Security
Compliance Standards
- CIS Kubernetes
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify the current CNI plugin and its capabilities
- Run on: any machine with kubectl access
- Commands:
kubectl get pods -n kube-system -o widekubectl get daemonset -n kube-systemkubectl get pods -n kube-system -l k8s-app=calico-node --ignore-not-foundkubectl get pods -n kube-system -l k8s-app=weave-net --ignore-not-foundkubectl get pods -n kube-system -l k8s-app=cilium --ignore-not-foundkubectl get pods -n kube-system -l k8s-app=flannel --ignore-not-foundkubectl get pods -n kube-system -l k8s-app=kube-router --ignore-not-found
- From these outputs, determine which CNI is installed and consult its official documentation to verify whether it implements Kubernetes
NetworkPolicy.
-
Check whether NetworkPolicy objects are being used
- Run on: any machine with kubectl access
- Commands:
kubectl get networkpolicy --all-namespaces
- If no
NetworkPolicyresources exist, document that policies are not in use and decide whether you want to start using them. If they exist, proceed to validate that they are enforced.
-
Functionally test whether NetworkPolicies are enforced
- Run on: any machine with kubectl access
- Create a test namespace and pods:
kubectl create namespace np-testkubectl run np-allowed -n np-test --image=nginx --port=80 --labels=role=allowed --restart=Neverkubectl run np-denied -n np-test --image=nginx --port=80 --labels=role=denied --restart=Neverkubectl run np-client -n np-test --image=busybox --restart=Never --command -- sh -c "sleep 3600"kubectl wait --for=condition=ready pod -n np-test --all --timeout=120s
- Verify baseline connectivity (should be allowed before any policy):
kubectl exec -n np-test np-client -- wget -qO- http://np-allowed.np-test.svc.cluster.localkubectl exec -n np-test np-client -- wget -qO- http://np-denied.np-test.svc.cluster.local
-
Apply a restrictive NetworkPolicy and observe behavior
- Run on: any machine with kubectl access
- Create a policy that only allows traffic to
np-allowedfrom pods with labelaccess=granted:cat <<'EOF' | kubectl apply -f -apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: allow-from-granted-onlynamespace: np-testspec:podSelector:matchLabels:role: allowedpolicyTypes:- Ingressingress:- from:- podSelector:matchLabels:access: grantedEOF - Label the client incorrectly and test (should be blocked if NetworkPolicy is enforced):
kubectl label pod -n np-test np-client access=denied --overwritekubectl exec -n np-test np-client -- wget -qO- http://np-allowed.np-test.svc.cluster.local --timeout=5
- Then label it correctly and test again (should succeed if enforced):
kubectl label pod -n np-test np-client access=granted --overwritekubectl exec -n np-test np-client -- wget -qO- http://np-allowed.np-test.svc.cluster.local --timeout=5
- If connectivity is unaffected by the policy, your current CNI is not enforcing
NetworkPolicy.
-
Decide and plan remediation if NetworkPolicies are not enforced
- If the CNI does not support
NetworkPolicy, or the tests above show no enforcement:- Document the current CNI and its limitations.
- Select a CNI that supports Kubernetes
NetworkPolicyand is compatible with your environment (for example, Calico, Cilium, Weave Net, Kube-router; verify support in their documentation). - Plan a migration window, including:
- Reading the chosen CNI’s install/migration guide for your Kubernetes distribution.
- Capturing current CNI manifests in
kube-system:kubectl get daemonset,deploy,cm,svc -n kube-system -o widekubectl get daemonset -n kube-system -o yaml > /tmp/kube-system-daemonsets.yaml - Assessing impact on existing workloads and any current (even if non‑enforced)
NetworkPolicyobjects.
- If the CNI does not support
-
Clean up test resources and re-verify after any CNI change
- Run on: any machine with kubectl access
- Clean up test namespace:
kubectl delete namespace np-test
- After migrating to a CNI that supports
NetworkPolicy, repeat steps 2–4 to confirm that:NetworkPolicyobjects exist (if you use them), and- Connectivity tests change as expected when policies are applied, demonstrating that the CNI now enforces network policies.
Using kubectl
# 1. Identify which CNI plugin is in use
# Run on: any machine with kubectl access
kubectl get pods -n kube-system -o wide | egrep 'calico|cilium|weave|flannel|canal|antrea|azure-cni|amazon-vpc-cni|gke|ovn|tigera|cni'
# Also list all DaemonSets in kube-system (most CNIs run as a DaemonSet)
kubectl get daemonsets -n kube-system -o wide
What to look for
- Pods/DaemonSets named like:
calico-node,calico-typha,tigera-operator→ Calico (supports NetworkPolicy)cilium,cilium-agent→ Cilium (supports NetworkPolicy)kube-flannel-dsorflannel-*→ Flannel (basic Flannel does NOT enforce NetworkPolicy)weave-net→ Weave Net (supports NetworkPolicy)kube-router→ kube-router (supports NetworkPolicy)antrea-agent→ Antrea (supports NetworkPolicy)- Cloud CNIs (
amazon-vpc-cni-*,azure-cni-*,gke-*,ovn-kubernetes, etc.) → check their docs for NetworkPolicy support.
- If you see only
kube-proxyand no obvious CNI pods, or see a home‑grown/unknown CNI without clear documentation, treat this as “unknown/likely not supporting NetworkPolicy” until verified.
# 2. Check whether any NetworkPolicy objects exist
# Run on: any machine with kubectl access
kubectl get networkpolicy --all-namespaces
What to look for
- If no NetworkPolicy objects exist but:
- Security requirements expect pod‑to‑pod isolation, and
- The CNI supports NetworkPolicy
→ This is a policy design gap, not a CNI capability problem.
- If many
NetworkPolicyobjects exist, but the CNI is known not to support them (e.g., plain Flannel):- This indicates a serious problem: policies are being defined but are not enforced at all.
# 3. Inspect an example NetworkPolicy spec
# Run on: any machine with kubectl access
# Replace NAMESPACE and NAME with a policy you saw above
kubectl get networkpolicy -n NAMESPACE NAME -o yaml
What to look for
- Confirm these are Kubernetes
kind: NetworkPolicy(not a CRD from some other system). - If the CNI is known not to support NetworkPolicy, any such spec is effectively documentation only, not enforced.
# 4. (Optional) Quick runtime sanity check of isolation behavior
# Run on: any machine with kubectl access
# 4a. Create a test namespace
kubectl create namespace np-test
# 4b. Create two simple test pods
kubectl run client -n np-test --image=busybox --restart=Never -- /bin/sh -c "sleep 3600"
kubectl run server -n np-test --image=nginx --restart=Never
# Wait for them to be Ready
kubectl wait --for=condition=Ready pod/client pod/server -n np-test --timeout=120s
# 4c. Get the server Pod IP
SERVER_IP=$(kubectl get pod server -n np-test -o jsonpath='{.status.podIP}')
echo "$SERVER_IP"
# 4d. Test connectivity BEFORE any NetworkPolicy (should generally succeed in typical clusters)
kubectl exec -n np-test client -- wget -qO- "http://$SERVER_IP"
# 4e. Apply a default-deny NetworkPolicy in np-test
cat << 'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: np-test
spec:
podSelector: {}
policyTypes:
- Ingress
EOF
# 4f. Test connectivity AGAIN after policy is applied
kubectl exec -n np-test client -- wget -qO- "http://$SERVER_IP" || echo "connection blocked"
What indicates a problem
- If the initial connection (step 4d) fails unexpectedly → there may already be other policies or other controls in place; interpret with caution.
- Key check: after applying the
deny-allNetworkPolicy (step 4e):- If the wget in step 4f still succeeds consistently and you are sure:
- The
deny-allpolicy is applied (check withkubectl get networkpolicy -n np-test deny-all -o yaml), and - There are no other NetworkPolicies allowing this traffic
→ This strongly suggests the CNI does not enforce Kubernetes NetworkPolicy (or is misconfigured).
- The
- If the wget fails or hangs until timeout, this suggests NetworkPolicies are being enforced.
- If the wget in step 4f still succeeds consistently and you are sure:
Clean up the test objects when done:
kubectl delete namespace np-test
# 5. Verification summary command (for documentation)
# Run on: any machine with kubectl access
# Summarize CNI-related pods and whether NetworkPolicies exist
echo "=== CNI Pods (kube-system) ==="
kubectl get pods -n kube-system -o wide | egrep 'calico|cilium|weave|flannel|canal|antrea|azure-cni|amazon-vpc-cni|gke|ovn|tigera|cni' || echo "No known CNI pods found"
echo "=== NetworkPolicies in the cluster ==="
kubectl get networkpolicy --all-namespaces || echo "No NetworkPolicies defined"
How to interpret
- If the CNI identified above is one that does not support NetworkPolicy (e.g., plain Flannel) and there is a requirement to restrict pod‑to‑pod traffic using Kubernetes
NetworkPolicy, this finding is not remediated and you must:- Migrate to a CNI that supports NetworkPolicies, or
- Implement alternative network controls outside Kubernetes.
Automation
#!/usr/bin/env bash
#
# Check CNI plugins in use and whether NetworkPolicies are likely to be enforced
# Run on: any machine with kubectl access and cluster-wide RBAC
#
# Requires: kubectl, jq
set -euo pipefail
echo "=== 1) Detect CNI plugins in use on each node ==="
kubectl get nodes -o wide | sed 's/^/NODE: /'
echo
echo "Node-level CNI detection (from kubelet args and CNI config paths):"
kubectl get nodes -o name | while read -r node; do
nodename="${node##*/}"
echo
echo "---- ${nodename} ----"
# Try to detect kubelet CNI-related flags from the kubelet config (if present)
# This only works on some distros that expose config via the ConfigMap
if kubectl -n kube-system get cm kubelet-config -o json >/dev/null 2>&1; then
echo " kubelet CNI configuration (cluster-wide, from kubelet-config ConfigMap):"
kubectl -n kube-system get cm kubelet-config -o json \
| jq -r '.data | to_entries[] | select(.key|test("kubelet")) | .value' \
| jq -r '.cniConfDir? // "<unknown>", .cniBinDir? // "<unknown>"' \
| awk 'NR==1{printf " cniConfDir: %s\n",$0} NR==2{printf " cniBinDir: %s\n",$0}'
else
echo " kubelet-config ConfigMap not found; CNI paths may need to be checked on nodes directly."
fi
# Look for CNI-related DaemonSets on this node via nodeSelector / tolerations
echo " CNI-related DaemonSets scheduled on this node:"
kubectl get daemonsets -A -o json \
| jq -r --arg NODE "${nodename}" '
.items[]
| select(.metadata.name|test("calico|cilium|weave|flannel|antrea|canal|ovn|kube-ovn";"i"))
| "\(.metadata.namespace)/\(.metadata.name)"' \
| sed 's/^/ /' || true
done
echo
echo "=== 2) Detect installed CNI plugin components (best-effort from known labels) ==="
echo "Known network/CNI DaemonSets:"
kubectl get daemonsets -A \
| grep -Ei 'calico|cilium|weave|flannel|antrea|canal|ovn|kube-ovn|kube-router|tigera' \
|| echo " No known CNI DaemonSets matched by heuristic grep."
echo
echo "For each candidate CNI, basic capability hints:"
kubectl get daemonsets -A -o json \
| jq -r '
.items[]
| select(.metadata.name|test("calico|cilium|weave|flannel|antrea|canal|ovn|kube-ovn|kube-router|tigera";"i"))
| "- CNI: \(.metadata.namespace)/\(.metadata.name)
Images: \(.spec.template.spec.containers[].image)
Annotations: \(.metadata.annotations // {} | to_entries[]? | select(.key|test(\"networkpolicy|network-policy|policies\";\"i\")) | .key + \"=\" + .value)"' \
| sed 's/^[ ]\+//'
echo
echo "=== 3) Detect presence of NetworkPolicy objects in the cluster ==="
kubectl get networkpolicies --all-namespaces || echo "No NetworkPolicies found (no output)."
echo
echo "=== 4) Probe whether basic egress isolation is likely enforced (heuristic) ==="
echo "NOTE: This does NOT prove correctness; it only checks if any default-deny policies exist."
echo "Default-deny ingress NetworkPolicies:"
kubectl get networkpolicies --all-namespaces -o json \
| jq -r '
.items[]
| select(
(.spec.podSelector | has("matchLabels") or has("matchExpressions"))
and
((.spec.ingress | length == 0) or (.spec.ingress == null))
)
| "\(.metadata.namespace)/\(.metadata.name)"' \
|| echo " None detected."
echo
echo "Default-deny egress NetworkPolicies:"
kubectl get networkpolicies --all-namespaces -o json \
| jq -r '
.items[]
| select(
(.spec.podSelector | has("matchLabels") or has("matchExpressions"))
and
((.spec.egress | length == 0) or (.spec.egress == null))
and
(.spec.policyTypes[]? | ascii_downcase == "egress")
)
| "\(.metadata.namespace)/\(.metadata.name)"' \
|| echo " None detected."
echo
echo "=== 5) Summary of potential issues to review manually ==="
echo "1) If no CNI DaemonSet or plugin could be identified above, manually check:"
echo " - Which CNI is installed on each node (e.g., /etc/cni/net.d on the node)."
echo " - Whether that CNI's documentation states explicit support for Kubernetes NetworkPolicy."
echo
echo "2) If the identified CNI is known NOT to support NetworkPolicies (e.g., vanilla Flannel),"
echo " this is a finding: NetworkPolicies defined in the cluster will NOT be enforced."
echo
echo "3) If NetworkPolicy objects exist but the CNI plugin does not support them, this is a finding."
echo " Compare the CNIs detected in section (2) against vendor docs."
echo
echo "4) If no NetworkPolicy objects exist at all, this script cannot tell whether the CNI"
echo " would enforce them; you must still confirm the plugin's capabilities from its docs."
echo
echo "This check is MANUAL: use the above data + the CNI vendor documentation to decide whether"
echo "the CNI in use truly supports Kubernetes NetworkPolicies."
How to interpret output (what indicates a problem):
- No CNI DaemonSet or plugin is detected, or only a plugin known not to support NetworkPolicies is present (for example, basic Flannel without policy extensions).
- NetworkPolicy objects exist (
kubectl get networkpolicies --all-namespacesshows entries), but the detected CNI plugin’s documentation does not explicitly state support for Kubernetes NetworkPolicy. - Operators cannot identify any CNI plugin at all or cannot confirm from documentation that it enforces NetworkPolicies.