Ensure CNI Plugin Supports Network Policies
More Info:
The CNI plugin must support NetworkPolicy resources for traffic segmentation to be enforceable. Use a CNI plugin that supports network policies and follow least-privilege network design.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS EKS
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Confirm the CNI plugin and its NetworkPolicy support
- Run on: any machine with kubectl access
- Commands:
kubectl get pods -n kube-system -o wide | egrep 'calico|cilium|weave|flannel|antrea|kube-router'kubectl get daemonset -n kube-system -o wide
- Review:
- Identify the CNI in use (e.g., Calico, Cilium, Antrea) from pod/DaemonSet names.
- Cross-check its documentation to confirm it supports Kubernetes
NetworkPolicy(and whether it supports both ingress and egress).
-
Check whether NetworkPolicy objects are in use at all
- Run on: any machine with kubectl access
- Commands:
kubectl get networkpolicy --all-namespaces
- Review:
- If no
NetworkPolicyresources exist, the cluster currently has no enforced traffic segmentation. You must design and deploy policies (starting with least-privilege) once you confirm the CNI supports them.
- If no
-
Identify namespaces lacking a default restrictive policy
- Run on: any machine with kubectl access
- Commands:
# List all namespaceskubectl get ns# For each namespace, list its policies (example for 'default'):kubectl get networkpolicy -n default -o yaml
- Review for each namespace:
- Check whether there is at least one “default deny” style policy that selects all pods and denies all ingress and (if supported) egress by default. Typical patterns:
- PodSelector is empty (
podSelector: {}) to match all pods. - No
ingresssection, orpolicyTypesincludesIngressbut no allowed peers. - For egress control,
policyTypesincludesEgressand either noegressrules or only narrowly scoped destinations.
- PodSelector is empty (
- Check whether there is at least one “default deny” style policy that selects all pods and denies all ingress and (if supported) egress by default. Typical patterns:
-
Assess current policies against least-privilege
- Run on: any machine with kubectl access
- Commands:
kubectl get networkpolicy --all-namespaces -o yaml > /tmp/all-networkpolicies.yaml
- Review
/tmp/all-networkpolicies.yaml:- Look for broad rules such as:
podSelector: {}combined withingressfrom all sources oregressto0.0.0.0/0.namespaceSelector: {}without additional constraints.
- Determine whether each policy exists to explicitly allow necessary traffic given a default-deny stance, or if it unintentionally creates wide-open access.
- Look for broad rules such as:
-
Design and apply least-privilege “default deny” and scoped allow policies
- Run on: any machine with kubectl access
- For a namespace missing default-deny, create a manifest like:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: default-deny-allnamespace: defaultspec:podSelector: {}policyTypes:- Ingress- Egress
- Apply:
kubectl apply -f default-deny-all.yaml
- Then, iteratively define additional
NetworkPolicymanifests that:- Select only the pods that must communicate.
- Allow only specific ingress sources and/or egress destinations and ports required for application function.
-
Verify enforcement and adjust as needed
- Run on: any machine with kubectl access
- Commands:
kubectl get networkpolicy --all-namespaces -o widekubectl describe networkpolicy -n default default-deny-all
- Optional functional check:
- Use test pods (e.g.,
kubectl run -it --rm test --image=busybox --restart=Never -- sh) to attempt connections that should be blocked or allowed, confirming the CNI is enforcingNetworkPolicyas designed. Adjust policies if legitimate traffic is unintentionally denied.
- Use test pods (e.g.,
Using kubectl
# 1) List all installed CNI plugins (pods in kube-system)
# Run on: any machine with kubectl access
kubectl get pods -n kube-system -o wide
Look for pods with names like calico-*, cilium-*, azure-cni-*, aws-node, weave-net, antrea-*, etc.
Investigate the documented capabilities of the detected CNI. If it does not explicitly support Kubernetes NetworkPolicy (or you see only a basic CNI like flannel without policy add-ons), network policies will not be enforced.
# 2) Check if any NetworkPolicies exist cluster-wide
kubectl get networkpolicies --all-namespaces
If this returns No resources found (or very few policies in only one namespace), then there is likely no meaningful segmentation. This is a policy gap even if the CNI supports NetworkPolicy.
# 3) Inspect network policies in a given namespace
# Replace NAMESPACE with a real namespace, e.g. "default"
kubectl get networkpolicy -n NAMESPACE -o wide
kubectl describe networkpolicy -n NAMESPACE POLICY_NAME
kubectl get networkpolicy -n NAMESPACE -o yaml
Things that indicate a problem:
- No “default deny” style policy (e.g. policies that select many/all pods and deny all ingress/egress by default).
- Policies only allow traffic but never restrict it (no baseline deny).
podSelector: {}with overly broadingress/egressrules that effectively allow all.
# 4) Verify whether default namespaces have baseline protection
kubectl get ns
# For each namespace of interest, inspect its policies:
for ns in default kube-system kube-public; do
echo "Namespace: $ns"
kubectl get networkpolicy -n "$ns"
done
If default or other application namespaces have no NetworkPolicy objects at all, pods there can communicate freely with each other and, often, with the internet. That conflicts with a least-privilege design.
# 5) Test that policies are enforced (requires existing policies)
# 5a) Get two pods in the same namespace
kubectl get pods -n NAMESPACE -o wide
# 5b) From one pod, try reaching another (adjust POD_NAME and TARGET_IP)
kubectl exec -n NAMESPACE -it POD_NAME -- ping -c 3 TARGET_IP
kubectl exec -n NAMESPACE -it POD_NAME -- nc -vz TARGET_IP TARGET_PORT
If you have a NetworkPolicy that should deny this traffic but pings / connections still succeed, that suggests:
- The CNI plugin does not enforce NetworkPolicy, or
- The policy is misconfigured (e.g. selectors don’t match pods).
# 6) Check if a "default deny" exists in important namespaces
# Example label-based search (may or may not return results)
kubectl get networkpolicy -A -l "policy-type=default-deny" -o yaml
If no namespaces have any “deny all” style policy (by label or by manual inspection of specs), then the cluster likely lacks a least-privilege baseline; traffic is broadly allowed until explicitly denied.
Use the findings above to decide:
- Whether your current CNI needs to be replaced/upgraded to one that supports NetworkPolicy.
- Where to design and apply deny-all (or global) policies as a baseline and then add narrowly scoped allow rules.
Automation
#!/usr/bin/env bash
# Purpose:
# - Report which CNI is installed
# - Check if it is one that supports NetworkPolicy
# - Summarize NetworkPolicy usage by namespace
#
# Run on: any machine with kubectl access and cluster-admin permissions
set -euo pipefail
echo "=== 1. Detecting CNI plugin(s) ==="
echo "-> Inspecting kube-system namespace for common CNI DaemonSets and Pods..."
kubectl get ds,pod -n kube-system -o wide | grep -Ei 'calico|cilium|weave|flannel|canal|antrea|ovn|amazon-vpc-cni|aws-node|azure|gke|cni' || \
echo "No obvious CNI DaemonSet/Pod found by name pattern."
echo
echo "=== 1a. Detailed CNI pod images (kube-system) ==="
kubectl get pods -n kube-system -o jsonpath='{range .items[*]}{.metadata.name}{" "}{range .spec.containers[*]}{.image}{" "}{end}{"\n"}{end}' \
| grep -Ei 'calico|cilium|weave|flannel|canal|antrea|ovn|cni|aws-node|azure|gke' || \
echo "No obvious CNI container images matched."
cat <<'EOF'
INTERPRETATION (CNI):
- You MUST confirm from documentation whether your detected CNI supports Kubernetes NetworkPolicy.
- Common cases:
* Supports NetworkPolicy: calico, cilium, antrea, weave-net (with NP), canal, ovn-kubernetes, GKE-native, Azure CNI (with NP), etc.
* Does NOT enforce NetworkPolicy (or only partially): basic flannel, some cloud-native CNIs unless NP mode is enabled.
- If you see only 'flannel' or a cloud CNI known not to enforce NetworkPolicy, traffic segmentation via NetworkPolicy is NOT effective -> PROBLEM.
EOF
echo "=== 2. Cluster-wide NetworkPolicy usage ==="
echo "-> Count NetworkPolicy objects per namespace"
echo "NAMESPACE NP_COUNT"
kubectl get networkpolicy --all-namespaces --no-headers 2>/dev/null \
| awk '{print $1}' | sort | uniq -c | awk '{printf "%-10s %s\n", $2, $1}' || \
echo "No NetworkPolicy resources found in any namespace."
echo
echo "=== 2a. Namespaces with ZERO NetworkPolicies ==="
# List all namespaces, join with those that have NPs
ns_with_np="$(kubectl get networkpolicy --all-namespaces --no-headers 2>/dev/null | awk '{print $1}' | sort -u || true)"
echo "Namespaces without any NetworkPolicy defined:"
kubectl get ns --no-headers | awk '{print $1}' | while read -r ns; do
if ! grep -qx "$ns" <<< "$ns_with_np"; then
echo " - $ns"
fi
done
cat <<'EOF'
INTERPRETATION (NetworkPolicy presence):
- Namespaces with ZERO NetworkPolicies are likely allowing all traffic by default.
- If your CNI DOES enforce NetworkPolicy, you should:
* At least have baseline policies in sensitive namespaces, and
* Consider a namespace-wide "default deny" (ingress/egress) where appropriate.
EOF
echo "=== 3. Detect namespaces missing a 'default deny' style policy ==="
echo "-> Heuristic: look for NetworkPolicies that set podSelector: {} and policyTypes including Ingress/Egress"
echo
echo "Namespaces and policies that LOOK like 'default deny' candidates:"
kubectl get networkpolicy --all-namespaces -o json \
| jq -r '
.items[]
| select(
(.spec.podSelector | tostring) == "{}"
)
| [
.metadata.namespace,
.metadata.name,
( .spec.policyTypes // [] | join(",") ),
( ( .spec.ingress // [] | length ) | tostring ),
( ( .spec.egress // [] | length ) | tostring )
]
| @tsv
' 2>/dev/null \
| awk 'BEGIN{printf "%-20s %-30s %-20s %-8s %-8s\n","NAMESPACE","POLICY","POLICY_TYPES","INGRESS","EGRESS"}
{printf "%-20s %-30s %-20s %-8s %-8s\n",$1,$2,$3,$4,$5}' || \
echo "No NetworkPolicies found that look like namespace-wide defaults."
cat <<'EOF'
INTERPRETATION (default deny):
- A robust least-privilege design usually:
* Starts with a namespace-wide deny-all (podSelector: {} and policyTypes: [Ingress,Egress]),
* Then adds specific allow rules.
- Potential PROBLEMS:
* Sensitive namespaces (e.g., containing workloads with secrets or external exposure) have:
- No NetworkPolicies at all, or
- Only allow rules but no clear default deny foundation.
* The CNI does not enforce NetworkPolicy, making any defined policies ineffectual.
EOF
echo "=== 4. Verification summary ==="
echo "- Confirm from vendor docs that the detected CNI ENFORCES NetworkPolicy."
echo "- Review:"
echo " * Namespaces with no NetworkPolicies"
echo " * Namespaces lacking a clear default deny-style policy"
echo "If CNI does not support NetworkPolicy, or policies do not follow least privilege, this check is NOT satisfied."
Problem indicators in the script output:
- CNI detection only shows plugins known not to enforce NetworkPolicy (e.g., plain flannel, or a cloud CNI without NP support enabled).
No NetworkPolicy resources found in any namespace.for a production cluster.- Sensitive namespaces listed under “Namespaces without any NetworkPolicy defined”.
- No or very few “default deny”-style policies in the heuristic section for critical namespaces.