Ensure Network Policy Is Enabled And Set Appropriately
More Info:
Enable a network policy engine such as Calico to segment and isolate pod traffic, enforcing least-privilege network communication within the cluster.
Risk Level
High
Address
Security
Compliance Standards
- CIS AKS
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify whether network policy is enabled on the AKS cluster
- Run on: any machine with Azure CLI access and permission on the subscription.
- Command (replace the placeholders with your real values before running):
az aks show \--resource-group <RESOURCE_GROUP_NAME> \--name <CLUSTER_NAME> \--query '{networkPlugin:networkProfile.networkPlugin, networkPolicy:networkProfile.networkPolicy}' \--output table
- Review:
- Confirm
networkPluginis typicallyazureorkubenet. - Confirm
networkPolicyis set tocalicoorazure. If it isnullor empty, network policy is not enabled.
- Confirm
-
Decide if change is required based on security requirements
- If
networkPolicyis not set, or set inconsistently with your standard (e.g., you mandate Calico but seeazure), determine:- Compliance requirements for pod-to-pod and pod-to-external isolation.
- Whether application teams rely on unrestricted intra-cluster communication that could be broken by restrictive policies.
- Document the required target state (e.g., “All AKS clusters must use networkPolicy = azure” or “= calico”).
- If
-
Review existing Kubernetes NetworkPolicies to understand current behavior
- Run on: any machine with kubectl access to the cluster.
- Commands:
kubectl get networkpolicy --all-namespaces
- If there are few or no policies, plan for a staged rollout: start with default-allow policies, then tighten.
- Identify critical namespaces/workloads that must remain reachable (ingress controllers, DNS, monitoring, logging, gateways).
-
Plan and apply the configuration change via cloud/IaC tooling
- Network policy enablement on AKS is a control-plane / cluster-creation-level setting; on many cluster versions it cannot be toggled in-place without recreation.
- If your cluster/IaC stack supports changing
networkPolicyin-place (check your current AKS version and Azure docs):- Example using Azure CLI (update scenario, if supported):
az aks update \--resource-group <RESOURCE_GROUP_NAME> \--name <CLUSTER_NAME> \--network-policy <azure|calico>
- Example using Azure CLI (update scenario, if supported):
- If in-place change is not supported, plan to:
- Update IaC (e.g., ARM/Bicep/Terraform) to include the desired
networkPolicyfor a new cluster. - Migrate workloads to the new cluster following your change-management process.
- Update IaC (e.g., ARM/Bicep/Terraform) to include the desired
-
Design and deploy baseline NetworkPolicies for least-privilege
- Using kubectl or your GitOps/IaC pipeline, implement at least:
- Namespaced default policies (e.g., a default “deny-all-ingress” and “deny-all-egress” or “allow-necessary-egress-only”) in sensitive namespaces.
- Explicit allow policies for:
- DNS to kube-dns/CoreDNS.
- Ingress from load balancer/ingress controllers to exposed services.
- Monitoring/logging agents communicating with their backends.
- Apply these in a test environment first, then progressively in production namespaces.
- Using kubectl or your GitOps/IaC pipeline, implement at least:
-
Verify and document the final state
- Re-run the Azure CLI check:
az aks show \--resource-group <RESOURCE_GROUP_NAME> \--name <CLUSTER_NAME> \--query '{networkPlugin:networkProfile.networkPlugin, networkPolicy:networkProfile.networkPolicy}' \--output table
- Confirm
networkPolicyreflects the intended engine. - Re-run:
kubectl get networkpolicy --all-namespaces
- Validate that critical application paths still function and that unnecessary pod-to-pod communication is blocked according to your baseline policies. Document the result and any exceptions.
- Re-run the Azure CLI check:
Using kubectl
kubectl cannot be used to enable or change the cluster-wide network policy engine in AKS; this is configured at the cloud provider / managed control plane level (for example via the Azure portal, az aks, or IaC such as ARM/Bicep/Terraform). Refer to the Manual Steps section for guidance on enabling and configuring a network policy provider like Calico for your cluster.
Automation
#!/usr/bin/env bash
# Automation: Assess AKS Network Policy Enablement and Usage
# Requirements: az, kubectl, jq
# Run location: any machine with Azure CLI and kubectl access
set -euo pipefail
echo "=== 1. CLUSTER-LEVEL: AKS networkPolicy configuration ==="
# List all AKS clusters in the subscription with their networkPolicy setting
echo "[INFO] Listing AKS clusters and their network policy mode..."
az aks list -o json | jq -r '
.[] | [
.name,
.resourceGroup,
(.networkProfile.networkPolicy // "null"),
(.networkProfile.networkPlugin // "null")
] | @tsv' | column -t
cat <<'EOF'
[INTERPRETATION]
- networkPolicy == "azure" or "calico": a network policy engine is enabled at the AKS control-plane level.
- networkPolicy == "null" or empty: NO network policy engine is enabled on the cluster (finding).
- networkPlugin:
- "azure" or "kubenet" are compatible with Azure network policies, but you must still have networkPolicy set.
EOF
echo
echo "=== 2. CLUSTER-LEVEL: Detailed view for a specific cluster (optional) ==="
read -r -p "Enter AKS resource group for a specific cluster (or leave blank to skip): " AKS_RG || true
if [ -n "${AKS_RG:-}" ]; then
read -r -p "Enter AKS cluster name: " AKS_NAME
echo
echo "[INFO] az aks show for ${AKS_NAME} in ${AKS_RG}..."
az aks show -g "${AKS_RG}" -n "${AKS_NAME}" -o json | jq '
{
name: .name,
resourceGroup: .resourceGroup,
kubernetesVersion: .kubernetesVersion,
networkPlugin: .networkProfile.networkPlugin,
networkPolicy: .networkProfile.networkPolicy,
networkMode: .networkProfile.networkMode
}'
cat <<'EOF'
[INTERPRETATION]
- If .networkPolicy is null or not present: network policies are NOT enabled on this AKS cluster (finding).
- If .networkPolicy is "azure" or "calico": a policy engine is enabled; proceed to namespace/pod-level review.
EOF
fi
echo
echo "=== 3. IN-CLUSTER: Namespaces lacking any NetworkPolicy ==="
echo "[INFO] Using kubectl context: $(kubectl config current-context 2>/dev/null || echo 'UNKNOWN')"
# Get namespaces that do NOT have any NetworkPolicy objects
echo
echo "[INFO] Discovering namespaces with zero NetworkPolicy objects..."
# Build a set of namespaces that have at least one NetworkPolicy
NS_WITH_NP=$(kubectl get networkpolicy --all-namespaces -o json 2>/dev/null \
| jq -r '.items[].metadata.namespace' | sort -u || true)
# Get all namespaces
ALL_NS=$(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
echo "Namespaces with NO NetworkPolicy objects:"
MISSING_COUNT=0
while read -r ns; do
if ! grep -qx "$ns" <<<"$NS_WITH_NP"; then
echo " - $ns"
MISSING_COUNT=$((MISSING_COUNT + 1))
fi
done <<<"$ALL_NS"
cat <<EOF
[INTERPRETATION]
- Any namespace listed above currently has ZERO NetworkPolicy resources.
- This typically indicates that pod traffic in those namespaces is wide open
(subject only to cloud-level or node-level controls), not segmented per least privilege.
- **Finding**: Namespaces that host sensitive workloads but appear here likely violate CIS AKS 5.4.4.
EOF
echo
echo "=== 4. IN-CLUSTER: Namespaces with NetworkPolicy and their default posture ==="
echo "[INFO] Listing namespaces that have at least one NetworkPolicy and summarizing default isolation..."
# For each namespace that has at least one NetworkPolicy, check if any policy selects all pods with default deny
for ns in $NS_WITH_NP; do
echo
echo "[NAMESPACE] $ns"
# List policies
kubectl get networkpolicy -n "$ns" -o wide || continue
# Identify potential 'default deny ingress' policies
DEFAULT_DENY_INGRESS=$(
kubectl get networkpolicy -n "$ns" -o json \
| jq -r '.items[]
| select(
((.spec.podSelector | has("matchLabels") | not) or (.spec.podSelector.matchLabels | length == 0))
and ((.spec.ingress | length == 0) or (.spec.policyTypes[]? == "Ingress"))
)
| .metadata.name' || true
)
# Identify potential 'default deny egress' policies
DEFAULT_DENY_EGRESS=$(
kubectl get networkpolicy -n "$ns" -o json \
| jq -r '.items[]
| select(
((.spec.podSelector | has("matchLabels") | not) or (.spec.podSelector.matchLabels | length == 0))
and ((.spec.egress | length == 0) or (.spec.policyTypes[]? == "Egress"))
)
| .metadata.name' || true
)
echo
if [ -n "$DEFAULT_DENY_INGRESS" ]; then
echo " Default-deny INGRESS policy candidates:"
sed 's/^/ - /' <<<"$DEFAULT_DENY_INGRESS"
else
echo " [WARNING] No clear default-deny INGRESS policy detected in this namespace."
fi
if [ -n "$DEFAULT_DENY_EGRESS" ]; then
echo " Default-deny EGRESS policy candidates:"
sed 's/^/ - /' <<<"$DEFAULT_DENY_EGRESS"
else
echo " [WARNING] No clear default-deny EGRESS policy detected in this namespace."
fi
cat <<'EOF_NS'
[INTERPRETATION]
- Presence of at least one 'default deny' policy (for ingress and/or egress) indicates
a stronger, least-privilege stance.
- Absence of any default deny policies suggests that traffic may still be overly permissive,
even though NetworkPolicy objects exist.
EOF_NS
done
echo
echo "=== 5. SUMMARY ==="
echo "Namespaces without any NetworkPolicy: ${MISSING_COUNT}"
cat <<'EOF'
[WHAT INDICATES A PROBLEM?]
1. At cluster level (az aks show / az aks list):
- networkPolicy is null, missing, or empty:
-> Network policy engine is NOT enabled on the AKS cluster (CIS AKS 5.4.4 finding).
2. At namespace level (kubectl checks):
- Namespaces with no NetworkPolicy at all:
-> Pods in those namespaces are likely not isolated by Kubernetes NetworkPolicy.
- Namespaces with NetworkPolicy but lacking any default-deny-style policies:
-> Policies may be too permissive; review against least-privilege requirements.
[NOTE]
This script only surfaces current configuration for review. Enabling or changing the
network policy engine for AKS must be done through Azure (az aks, portal, or IaC),
and per-namespace policy design requires manual security review.
EOF