Ensure Latest CNI Version Is Used
More Info:
The CNI plugin should support and enforce NetworkPolicy following least-privilege access. Start with a deny-all policy for inbound and outbound traffic and add exceptions as needed.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS OKE
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify namespaces and current NetworkPolicies
- Run on any machine with
kubectlaccess:kubectl get nskubectl get networkpolicy --all-namespaces - Review which namespaces lack any
NetworkPolicy, as they are effectively “allow all” for ingress/egress.
- Run on any machine with
-
Inspect existing NetworkPolicies for least-privilege design
- For each namespace with policies, inspect details:
kubectl get networkpolicy -n <namespace> -o yaml
- Check that:
- Policies are scoped to specific
podSelectors (not missing or overly broad where that would violate least privilege). ingress/egressrules are as narrow as possible (namespaces, pods, IPBlocks, ports).- There is no “catch-all allow” (e.g., empty
ingress/egressarrays interpreted as allow depending on CNI, or rules that match all peers and all ports).
- Policies are scoped to specific
- For each namespace with policies, inspect details:
-
Determine default behavior (deny vs allow) per namespace
- Confirm whether there is an explicit “default deny” for both ingress and egress in each namespace where least privilege is required. Typical examples (for review, not necessarily verbatim for your CNI):
# Example default deny ingresskubectl get networkpolicy -n <namespace> default-deny-ingress -o yaml || true# Example default deny egresskubectl get networkpolicy -n <namespace> default-deny-egress -o yaml || true
- If such policies do not exist, note that this namespace is likely in an “allow all” state unless your CNI enforces a different default.
- Confirm whether there is an explicit “default deny” for both ingress and egress in each namespace where least privilege is required. Typical examples (for review, not necessarily verbatim for your CNI):
-
Review CNI capabilities and NetworkPolicy support
- Check which CNI is installed and whether it fully supports Kubernetes
NetworkPolicyand, if applicable, global policies:kubectl get pods -n kube-system -o wide | grep -iE 'calico|cilium|weave|flannel|ovn|antrea'kubectl get ds -n kube-system -o yaml | grep -i 'cni' -A5 -B5 - Use the CNI documentation (outside this procedure) to confirm:
- Support for
NetworkPolicy(ingress and egress). - Support for “global” policies (e.g., Calico GlobalNetworkPolicy) if you plan to use cluster-wide default denies.
- Support for
- Check which CNI is installed and whether it fully supports Kubernetes
-
Design and apply least-privilege default-deny policies
- For each target namespace, decide whether to use:
- A namespace-scoped default deny (
NetworkPolicywith emptyingressand/oregresslists andpodSelector: {}) plus explicit allow policies, or - A global/default policy mechanism provided by your CNI (e.g., Calico global policy), if appropriate.
- A namespace-scoped default deny (
- Create or update manifests locally, then apply:
kubectl apply -f /absolute/path/to/your-reviewed-networkpolicies.yaml
- Ensure that for every namespace where you introduce default deny, you also define the minimal set of allow rules needed for application functionality.
- For each target namespace, decide whether to use:
-
Verify effective behavior and refine
- Confirm policies are in place:
kubectl get networkpolicy --all-namespaces
- Optionally test connectivity (from test pods) to verify that:
- Traffic is blocked by default where intended.
- Only approved communications succeed.
- Iterate by adjusting manifests and reapplying with
kubectl apply -f ...until the balance between least privilege and application requirements is acceptable.
- Confirm policies are in place:
Using kubectl
# 1) List all NetworkPolicies in all namespaces
# Run on: any machine with kubectl access
kubectl get networkpolicy --all-namespaces -o wide
What to look for (problem indicators)
- No
NetworkPolicyobjects at all, or critical namespaces (e.g.default, application namespaces) with 0 policies. - Namespaces where every policy has
policyTypesonly containingIngress(noEgress) or vice versa, implying one direction is completely unrestricted. - Namespaces that are security‑sensitive but lack any “default deny” style policies.
# 2) Inspect all NetworkPolicies, including their spec
kubectl get networkpolicy --all-namespaces -o yaml
What to look for (problem indicators)
- Namespaces with no policy that:
- Explicitly denies all ingress (no
ingressrules,policyTypesincludesIngress), and - Explicitly denies all egress (no
egressrules,policyTypesincludesEgress).
- Explicitly denies all ingress (no
- Policies that are clearly overly permissive, for example:
podSelector: {}combined withingress: []but no correspondingegressdefault deny (or vice versa).- Allow rules using
ipBlock: 0.0.0.0/0or very broad CIDRs. - Policies that select all pods in the namespace (
podSelector: {}) and allow all traffic (emptyingress/egresssections with missingpolicyTypesso they default to Ingress only).
# 3) Check which CNI plugin is in use and its version (helps judge capabilities)
kubectl get pods -n kube-system -o wide \
| grep -iE 'calico|cilium|weave|flannel|cni|antrea'
Then inspect the CNI daemonset or pods:
# Example: describe a suspected CNI DaemonSet
kubectl get daemonset -n kube-system
kubectl describe daemonset <cni-daemonset-name> -n kube-system
What to look for (problem indicators)
- A CNI plugin known not to support Kubernetes
NetworkPolicy, or configured in a mode that disables policy enforcement. - Very old image tags (e.g. obvious legacy versions) suggesting the plugin may lack current NetworkPolicy features or bug fixes.
# 4) For a specific namespace, review effective policy coverage
# Replace NAMESPACE with the namespace you care about
kubectl get networkpolicy -n NAMESPACE -o yaml
What to look for (problem indicators)
- No “baseline” policies that:
- Deny all ingress (
policyTypes: ["Ingress"]with emptyingress: []), and - Deny all egress (
policyTypes: ["Egress"]with emptyegress: []or combined["Ingress","Egress"]with both empty).
- Deny all ingress (
- Policies that only add allows without any default deny in that namespace, meaning pods with no matching policy remain fully open.
# 5) (Optional) For Calico-only clusters, check for any GlobalNetworkPolicies
kubectl get globalnetworkpolicies.crd.projectcalico.org -A -o yaml 2>/dev/null
What to look for (problem indicators)
- Absence of any global “default deny” style
GlobalNetworkPolicywhen you expect cluster‑wide least‑privilege enforcement. - Global policies that broadly allow all ingress/egress without fine‑grained selectors.
How to interpret the findings
- If you find namespaces (especially
defaultor production app namespaces) with no default deny for ingress and egress and only permissive or no policies, the cluster is not following least‑privilege. - If the CNI plugin is old or lacks NetworkPolicy support, then even well‑written policies may not be enforced as intended.
Deciding what policies to add or adjust depends on application requirements and risk appetite and must be done manually based on this evidence.
Automation
#!/usr/bin/env bash
#
# Purpose:
# Cluster-wide review of CNI and NetworkPolicy posture for CIS OKE 4.3.1
#
# Run on:
# Any machine with kubectl access and current-context pointing at the target cluster.
#
# Requirements:
# - kubectl in PATH
# - jq in PATH (optional but recommended; script degrades gracefully without it)
set -euo pipefail
echo "=== 1) Detect CNI implementation and version (cluster-wide) ==="
echo
# Try common CNI DaemonSets and custom-resources to infer CNI and versions.
# This is heuristic only; you must review results manually.
echo "- Searching for common CNI DaemonSets in kube-system..."
kubectl get ds -n kube-system 2>/dev/null | sed '1!{/calico\|cilium\|weave\|flannel\|aws-node\|azure-cni\|antrea\|ovn\|kube-router/!d}'
echo
echo "- Detailed images for likely CNI DaemonSets:"
for ds in calico-node cilium aws-node azure-cni antrea-agent kube-flannel-ds weave-net kube-router ovnkube-node; do
if kubectl get ds "$ds" -n kube-system >/dev/null 2>&1; then
echo " * kube-system/DaemonSet/${ds}:"
kubectl get ds "$ds" -n kube-system -o jsonpath='{.spec.template.spec.containers[*].image}' 2>/dev/null
echo
fi
done
echo
echo "- Checking for common CNI CRDs (if present, also imply CNI type):"
kubectl get crd 2>/dev/null | sed '1!{/calico\|cilium\|antrea\|networkpolicies.crd.projectcalico.org/!d}' || true
echo
cat <<'EOF'
INTERPRETATION (possible problems):
- If no known CNI DaemonSet is found, determine manually which CNI you run and its version.
- Compare the listed CNI images/tags with vendor documentation to see if they are:
- Out of date or unsupported
- Missing NetworkPolicy support (some basic CNIs ignore NetworkPolicy).
- A problem for this control is:
- Using a CNI that does NOT implement Kubernetes NetworkPolicy, or
- Running obviously obsolete or deprecated CNI versions.
EOF
echo
echo "=== 2) List namespaces lacking any NetworkPolicy (no isolation defined) ==="
echo
# Namespaces with zero NetworkPolicy resources
all_ns=$(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
ns_with_np=$(kubectl get networkpolicy --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\n"}{end}' | sort -u)
problem_ns=()
for ns in $all_ns; do
if ! printf '%s\n' "$ns_with_np" | grep -qx "$ns"; then
problem_ns+=("$ns")
fi
done
echo "Namespaces with NO NetworkPolicy objects defined:"
if [ ${#problem_ns[@]} -eq 0 ]; then
echo " (none)"
else
for ns in "${problem_ns[@]}"; do
echo " - $ns"
done
fi
echo
cat <<'EOF'
INTERPRETATION (possible problems):
- Namespaces listed above have no NetworkPolicy at all.
- Under CIS guidance, you typically want at least a baseline deny policy
(or equivalent global policy) so unrestricted traffic is not allowed by default.
EOF
echo
echo "=== 3) Inspect baseline isolation: does each namespace have a 'default deny' policy? ==="
echo
# Heuristic check for default-deny-like policies per namespace.
default_deny_report() {
local ns="$1"
echo "Namespace: ${ns}"
# Get all NPs in namespace as JSON
if ! np_json=$(kubectl get networkpolicy -n "$ns" -o json 2>/dev/null); then
echo " Error: could not fetch NetworkPolicy in namespace."
return
fi
if command -v jq >/dev/null 2>&1; then
total=$(echo "$np_json" | jq '.items | length')
echo " NetworkPolicies: $total"
# Default deny ingress: spec.podSelector={} and no ingress or empty list
dd_ingress=$(echo "$np_json" | jq '[.items[]
| select(.spec.podSelector == {} and (.spec.ingress == null or .spec.ingress == []))
] | length')
# Default deny egress: spec.podSelector={} and no egress or empty list
dd_egress=$(echo "$np_json" | jq '[.items[]
| select(.spec.podSelector == {} and (.spec.egress == null or .spec.egress == []))
] | length')
echo " Default-deny-like ingress policies: $dd_ingress"
echo " Default-deny-like egress policies: $dd_egress"
else
echo " (jq not installed: printing raw policies for manual review)"
kubectl get networkpolicy -n "$ns" -o yaml
fi
echo
}
for ns in $all_ns; do
# Skip namespaces with no NPs (already flagged above)
if printf '%s\n' "$ns_with_np" | grep -qx "$ns"; then
default_deny_report "$ns"
fi
done
cat <<'EOF'
INTERPRETATION (possible problems):
- For each namespace:
- If "NetworkPolicies: 0", that namespace has NO isolation.
- If "Default-deny-like ingress policies: 0", inbound traffic is not globally denied by default.
- If "Default-deny-like egress policies: 0", outbound traffic is not globally denied by default.
- CIS guidance prefers:
- A starting point of deny-all for ingress AND egress at namespace level (or via CNI global policy),
with explicit allow rules layered on as needed.
- Note: Some CNIs (e.g., Calico) can enforce global policies outside NetworkPolicy;
review your CNI’s own policy objects in addition to this output.
EOF
echo
echo "=== 4) Quick summary of potential issues ==="
echo
echo "1) CNI / NetworkPolicy support:"
echo " - If no capable CNI is detected above or CNI version is very old, investigate and upgrade."
echo
echo "2) Namespaces with no NetworkPolicy:"
if [ ${#problem_ns[@]} -eq 0 ]; then
echo " - All namespaces have at least one NetworkPolicy."
else
for ns in "${problem_ns[@]}"; do
echo " - $ns"
done
fi
echo
echo "3) Default deny posture:"
echo " - Review per-namespace section above."
echo " - A problem exists where default-deny-like policies are missing AND no equivalent CNI-global policy exists."
echo
echo "This script is assessment-only. Apply fixes by:"
echo "- Validating/Upgrading the CNI to a NetworkPolicy-aware and supported version, and"
echo "- Designing and applying explicit deny-all + allow-exception NetworkPolicies (or CNI global policies) per your risk profile."