Ensure Network Policy Is Enabled And Set Appropriate
More Info:
Amazon EKS provides two ways to implement network policy. You choose a network policy option when you create an EKS cluster. The policy option cant be changed after the cluster is created: Calico Network Policies, an open-source network and network security solution founded by Tigera. Both implementations use Linux IPTables to enforce the specified policies. Policies are translated into sets of allowed and disallowed IP pairs. These pairs are then programmed as IPTable filter rules.
Risk Level
Low
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS Critical Security Controls v8
- CIS EKS
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- NIS2 Directive
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify the cluster’s network plugin / policy engine
- From any machine with AWS CLI access:
aws eks describe-cluster --name YOUR_CLUSTER_NAME --region YOUR_AWS_REGION \--query 'cluster.resourcesVpcConfig' --output json
- In the AWS console, open EKS → your cluster → Networking → confirm which CNI add-ons are installed (e.g., Amazon VPC CNI only, or Calico, etc.).
- If your cluster does not have a network policy–capable CNI (e.g., Calico), note that this cluster cannot enforce Kubernetes NetworkPolicies and you must either deploy a supported policy engine or plan a new cluster with it enabled.
- From any machine with AWS CLI access:
-
Check whether any Kubernetes NetworkPolicy objects exist
- From any machine with kubectl access:
kubectl get networkpolicy --all-namespaces
- If the list is empty but you require intra-cluster segmentation, you currently have no enforced NetworkPolicies even if a policy engine is present.
- From any machine with kubectl access:
-
Review current network segmentation requirements with stakeholders
- Document workloads that:
- Must only talk to specific namespaces/services (e.g., frontend → backend only).
- Must be internet-isolated or restricted to specific egress.
- Decide on a default stance per namespace: default-deny (preferred) vs allow-all.
- This is a business/architecture decision, not something a command can answer.
- Document workloads that:
-
If the cluster lacks a policy engine, plan provider-level remediation
- For existing EKS clusters created without a network policy–capable CNI, the network policy option cannot be switched in-place. Typical options are:
- Create a new EKS cluster with Calico or another supported network policy engine configured via your IaC (e.g., eksctl, Terraform, AWS CDK) or by following the provider’s “Install Calico on EKS” guidance.
- Migrate workloads to the new cluster, then decommission the old cluster.
- Capture this as a change plan (with maintenance windows, testing, and rollback).
- For existing EKS clusters created without a network policy–capable CNI, the network policy option cannot be switched in-place. Typical options are:
-
If a policy engine is present, implement / refine NetworkPolicies
- From any machine with kubectl access, inspect existing policies for a sample namespace:
kubectl get networkpolicy -n YOUR_NAMESPACE -o yaml
- Create or adjust NetworkPolicies (using your chosen GitOps/manifest process) to:
- Enforce namespace isolation (e.g., default-deny + explicit allows).
- Restrict ingress/egress based on the requirements from step 3.
- Apply and test connectivity carefully in a non‑production environment first.
- From any machine with kubectl access, inspect existing policies for a sample namespace:
-
Verify effective enforcement after changes
- From any machine with kubectl access, confirm policies are present and accepted:
kubectl get networkpolicy --all-namespaces
- Use provider/CNI-specific tools or logs (for Calico, e.g., calicoctl if in use, or pod connectivity tests using
kubectl execandcurl/nc) to validate that blocked connections actually fail and allowed connections succeed, documenting the results as evidence for this control.
- From any machine with kubectl access, confirm policies are present and accepted:
Using kubectl
kubectl cannot be used to enable or change the EKS cluster’s network policy provider; this is configured only at the cloud provider control-plane level (EKS console/CLI/IaC) when the cluster is created. Refer to the Manual Steps section for guidance on reviewing the current configuration and planning any required changes.
Automation
#!/usr/bin/env bash
#
# Purpose: Report Kubernetes network policy coverage across all namespaces.
# Scope: Run on any machine with kubectl context to the target cluster.
# Requires: kubectl, jq
#
# This does NOT verify whether EKS was created with Calico or another
# network policy engine; that must be checked in your IaC or EKS cluster
# creation configuration.
set -euo pipefail
if ! command -v kubectl >/dev/null 2>&1; then
echo "ERROR: kubectl not found in PATH" >&2
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
echo "ERROR: jq not found in PATH" >&2
exit 1
fi
echo "== Cluster info =="
kubectl version --short || true
echo
echo "== Namespaces and NetworkPolicy objects =="
# List all namespaces with count of NetworkPolicy objects and their types
kubectl get ns -o json | jq -r '
.items[] |
.metadata.name as $ns |
(
$ns,
(
( .metadata.labels // {} ) as $labels
| " labels: " + ( $labels | tojson )
),
(
" networkPolicies:",
(
[
( .metadata.name ) as $dummy
] |
. # just to make structure consistent
)
)
| @text
' >/dev/null 2>&1 || true
# More structured summary using multiple calls
echo "NAMESPACE,NUM_POLICIES,ISOLATED_PODS_DESCRIPTION"
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
num_policies=$(kubectl get networkpolicy -n "$ns" --no-headers 2>/dev/null | wc -l | tr -d ' ')
# Detect if any policy selects all pods (empty podSelector) which is typical for default deny
has_default_policy=$(kubectl get networkpolicy -n "$ns" -o json 2>/dev/null \
| jq '[.items[]
| select(.spec.podSelector == {} )
] | length > 0' 2>/dev/null || echo "false")
if [ "$num_policies" -eq 0 ]; then
iso_desc="NO NetworkPolicy objects; namespace likely fully non-isolated"
else
if [ "$has_default_policy" = "true" ]; then
iso_desc="Has at least one policy with empty podSelector (possible namespace-wide default policy)"
else
iso_desc="Has policies, but none with empty podSelector (isolation may be partial)"
fi
fi
echo "$ns,$num_policies,$iso_desc"
done
echo
echo "== Detailed per-namespace report =="
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
echo "----- Namespace: $ns -----"
kubectl get networkpolicy -n "$ns" || echo " (no NetworkPolicy objects)"
echo
done
echo "== Pod-level coverage check (sample) =="
# For each namespace, show how many pods are selected by at least one NetworkPolicy
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
total_pods=$(kubectl get pods -n "$ns" --no-headers 2>/dev/null | wc -l | tr -d ' ')
if [ "$total_pods" -eq 0 ]; then
echo "Namespace $ns: no pods"
continue
fi
# Build a label selector for pods matched by any policy in this namespace
policies_json=$(kubectl get networkpolicy -n "$ns" -o json 2>/dev/null || echo '{"items":[]}')
covered_pods=0
# If there are no policies, then 0 covered pods
num_policies=$(echo "$policies_json" | jq '.items | length')
if [ "$num_policies" -gt 0 ]; then
# For each pod, check if any policy's podSelector matches it
for pod in $(kubectl get pod -n "$ns" -o jsonpath='{.items[*].metadata.name}'); do
pod_labels=$(kubectl get pod "$pod" -n "$ns" -o json | jq '.metadata.labels // {}')
matched=$(echo "$policies_json" | jq --argjson podLabels "$pod_labels" '
[ .items[]
| .spec.podSelector.matchLabels // {}
| to_entries as $sel
| all($sel[]; $podLabels[.key] == .value)
] | any
')
if [ "$matched" = "true" ]; then
covered_pods=$((covered_pods + 1))
fi
done
fi
echo "Namespace $ns: pods covered by at least one NetworkPolicy = $covered_pods / $total_pods"
done
cat <<'EOF'
INTERPRETING OUTPUT:
1) "NAMESPACE,NUM_POLICIES,ISOLATED_PODS_DESCRIPTION"
- Namespaces with "NO NetworkPolicy objects" are likely fully non-isolated.
- Namespaces that "Has policies, but none with empty podSelector" may only
isolate specific pods; traffic for other pods remains unrestricted.
- Namespaces with "Has at least one policy with empty podSelector" often
have some form of namespace-wide baseline policy (e.g., default deny),
but you must review the actual spec to confirm.
2) "Detailed per-namespace report"
- If critical namespaces (for example, workloads handling sensitive data)
show "(no NetworkPolicy objects)", they likely do not have traffic
segmentation/enforcement in place.
3) "Pod-level coverage check (sample)"
- Shows how many pods in each namespace are selected by at least one
NetworkPolicy. A low ratio (near 0) in production namespaces is a
red flag that most pods are not governed by any policy.
LIMITATIONS:
- This script checks Kubernetes NetworkPolicy objects and which pods
they target. It does NOT:
* Confirm that the EKS cluster was created with Calico or any particular
network policy engine.
* Validate that policies enforce the "right" security posture.
If you see many namespaces with zero NetworkPolicy objects or critical
workload pods not covered by any policy, that indicates a potential
problem relative to the benchmark requirement to "segment and isolate
your traffic" using a network policy engine.
EOF