Skip to main content

Ensure That All Namespaces Have Network Policies Defined

More Info:

Use network policies to isolate traffic in your cluster network.

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)
  • Essential 8
  • ISO/IEC 27017
  • ISO/IEC 27018
  • ISO/IEC 27701
  • KSA PDPL
  • MAS Technology Risk Management (Singapore)
  • MITRE ATT&CK (Cloud)
  • 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

Manual Steps
  1. List namespaces that currently lack any NetworkPolicy (run on any machine with kubectl access):

    ns_without_np=$(kubectl get namespaces -o json | jq -r '.items[].metadata.name' | while read ns; do
    count=$(kubectl get networkpolicy -n "$ns" --no-headers 2>/dev/null | wc -l)
    if [ "$count" -eq 0 ]; then echo "$ns"; fi
    done)
    echo "$ns_without_np"
  2. For each namespace in the list, decide the default isolation behavior you want:

    • Most restrictive (recommended starting point): deny all ingress and egress by default, then create additional NetworkPolicies later to allow required flows.
    • Ingress-only isolation: deny all ingress but allow all egress.
    • Custom: define specific allowed selectors/ports/namespaces as needed.
  3. To apply a default deny all ingress and egress NetworkPolicy for one namespace (replace TARGET_NAMESPACE with the namespace name; run on any machine with kubectl access):

    kubectl apply -f - <<'EOF'
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
    name: default-deny-all
    namespace: TARGET_NAMESPACE
    spec:
    podSelector: {}
    policyTypes:
    - Ingress
    - Egress
    EOF
  4. If you prefer to only deny ingress and leave egress open for a namespace (replace TARGET_NAMESPACE; run on any machine with kubectl access):

    kubectl apply -f - <<'EOF'
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
    name: default-deny-ingress
    namespace: TARGET_NAMESPACE
    spec:
    podSelector: {}
    policyTypes:
    - Ingress
    EOF
  5. For critical namespaces (for example kube-system, monitoring, logging), review application requirements before applying default deny:

    • List pods and labels to plan selectors:
      kubectl get pods -n TARGET_NAMESPACE --show-labels
    • Start with default deny (Step 3 or 4), then iteratively add additional NetworkPolicies that:
      • Select specific pods via podSelector.matchLabels
      • Allow required ingress/egress based on from/to (e.g., namespaceSelector, podSelector, ipBlock) and ports.
  6. Verify that all namespaces now have at least one NetworkPolicy (run on any machine with kubectl access):

    ns_without_np=$(kubectl get namespaces -o json | jq -r '.items[].metadata.name' | while read ns; do
    count=$(kubectl get networkpolicy -n "$ns" --no-headers 2>/dev/null | wc -l)
    if [ "$count" -eq 0 ]; then echo "$ns"; fi
    done)
    if [ -z "$ns_without_np" ]; then
    echo "ALL_NAMESPACES_HAVE_NETWORK_POLICIES"
    else
    echo "NAMESPACES_WITHOUT_NETWORK_POLICIES: $ns_without_np"
    fi
Using kubectl

On any machine with kubectl access:

  1. Identify namespaces without NetworkPolicies (reuse the audit logic)
ns_without_np=$(kubectl get namespaces -o json | jq -r '.items[].metadata.name' | while read ns; do
count=$(kubectl get networkpolicy -n "$ns" --no-headers 2>/dev/null | wc -l)
if [ "$count" -eq 0 ]; then echo "$ns"; fi
done)

echo "$ns_without_np"
  1. For each listed namespace, create at least one NetworkPolicy. Below is a conservative default‑deny ingress and allow‑all egress policy you can apply per namespace (replace NAMESPACE_HERE each time):

Create a manifest file, for example:

cat << 'EOF' > default-netpol-namespace-here.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress-allow-all-egress
namespace: NAMESPACE_HERE
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress: []
egress:
- {}
EOF

Apply it to the target namespace:

kubectl apply -f default-netpol-namespace-here.yaml

Repeat editing namespace: NAMESPACE_HERE and applying for each namespace in $ns_without_np. Example loop to generate and apply identical policy to every namespace missing one:

for ns in $ns_without_np; do
cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress-allow-all-egress
namespace: $ns
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress: []
egress:
- {}
EOF
done
  1. Verification (same machine with kubectl):
ns_without_np=$(kubectl get namespaces -o json | jq -r '.items[].metadata.name' | while read ns; do
count=$(kubectl get networkpolicy -n "$ns" --no-headers 2>/dev/null | wc -l)
if [ "$count" -eq 0 ]; then echo "$ns"; fi
done)
if [ -z "$ns_without_np" ]; then
echo "ALL_NAMESPACES_HAVE_NETWORK_POLICIES"
else
echo "NAMESPACES_WITHOUT_NETWORK_POLICIES: $ns_without_np"
fi
Automation
#!/usr/bin/env bash
set -euo pipefail

# This script:
# - Finds all namespaces without any NetworkPolicy
# - Creates a default-deny NetworkPolicy in each such namespace
# - Is safe to re-run (it uses kubectl apply)
# - Verifies that all namespaces now have at least one NetworkPolicy
#
# Run on: any machine with kubectl access and jq installed.

# Fail fast if required tools are missing
for cmd in kubectl jq; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "ERROR: $cmd is required but not installed or not in PATH." >&2
exit 1
fi
done

# Optional: restrict to "active" namespaces (ignore those in Terminating phase)
namespaces_json=$(kubectl get namespaces -o json)

# Find namespaces with zero NetworkPolicies
ns_without_np=()
while IFS= read -r ns; do
# Skip namespaces that are Terminating
phase=$(echo "$namespaces_json" | jq -r ".items[] | select(.metadata.name==\"$ns\") | .status.phase")
if [ "$phase" != "Active" ]; then
continue
fi

count=$(kubectl get networkpolicy -n "$ns" --no-headers 2>/dev/null | wc -l | tr -d ' ')
if [ "$count" -eq 0 ]; then
ns_without_np+=("$ns")
fi
done < <(echo "$namespaces_json" | jq -r '.items[].metadata.name')

if [ "${#ns_without_np[@]}" -eq 0 ]; then
echo "ALL_NAMESPACES_ALREADY_HAVE_NETWORK_POLICIES"
else
echo "NAMESPACES_MISSING_NETWORK_POLICIES: ${ns_without_np[*]}"
echo "Creating default-deny NetworkPolicy in each missing namespace..."

for ns in "${ns_without_np[@]}"; do
cat <<EOF | kubectl apply -n "$ns" -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
EOF
done
fi

echo "Verifying that all namespaces have at least one NetworkPolicy..."

verify_ns_without_np=$(kubectl get namespaces -o json | jq -r '.items[].metadata.name' | while read -r ns; do
# Skip namespaces that are Terminating
phase=$(kubectl get namespace "$ns" -o jsonpath='{.status.phase}' 2>/dev/null || echo "")
if [ "$phase" != "Active" ]; then
continue
fi
count=$(kubectl get networkpolicy -n "$ns" --no-headers 2>/dev/null | wc -l | tr -d ' ')
if [ "$count" -eq 0 ]; then echo "$ns"; fi
done)

if [ -z "$verify_ns_without_np" ]; then
echo "ALL_NAMESPACES_HAVE_NETWORK_POLICIES"
exit 0
else
echo "NAMESPACES_STILL_WITHOUT_NETWORK_POLICIES: $verify_ns_without_np" >&2
exit 1
fi

Additional Reading: