Skip to main content

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

Manual Steps
  1. 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 networkPlugin is typically azure or kubenet.
      • Confirm networkPolicy is set to calico or azure. If it is null or empty, network policy is not enabled.
  2. Decide if change is required based on security requirements

    • If networkPolicy is not set, or set inconsistently with your standard (e.g., you mandate Calico but see azure), 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”).
  3. 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).
  4. 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 networkPolicy in-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>
    • If in-place change is not supported, plan to:
      • Update IaC (e.g., ARM/Bicep/Terraform) to include the desired networkPolicy for a new cluster.
      • Migrate workloads to the new cluster following your change-management process.
  5. 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.
  6. 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 networkPolicy reflects 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.
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