Skip to main content

Ensure 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 GKE
  • 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 are missing NetworkPolicies (run on any machine with kubectl access):

    (kubectl get ns -o json; kubectl get networkpolicy -A -o json) \
    | jq -rs '
    (.[0].items | map(.metadata.name)
    | map(select(.!="kube-system" and .!="kube-public" and .!="kube-node-lease"))) as $ns
    |
    ( (.[1].items // [])
    | sort_by(.metadata.namespace)
    | group_by(.metadata.namespace)
    | map({key: .[0].metadata.namespace, value: length})
    | from_entries
    ) as $np
    |
    [ $ns[] | select( ($np[.] // 0) == 0 ) ] as $missing
    |
    $missing[]'

    Note the namespace names returned; each needs at least one NetworkPolicy.

  2. For each namespace without a NetworkPolicy, create a default deny-all NetworkPolicy manifest file on your local machine (with kubectl access). Example for namespace example-namespace:

    cat > default-deny-all-example-namespace.yaml <<'EOF'
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
    name: default-deny-all
    namespace: example-namespace
    spec:
    podSelector: {}
    policyTypes:
    - Ingress
    - Egress
    EOF
  3. Apply the NetworkPolicy for that namespace (run on any machine with kubectl access):

    kubectl apply -f default-deny-all-example-namespace.yaml
  4. (Optional but recommended) For application namespaces where you need traffic, design and apply more granular NetworkPolicies that allow required ingress/egress. Example allow ingress from same namespace for example-namespace:

    cat > allow-same-namespace-example-namespace.yaml <<'EOF'
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
    name: allow-same-namespace
    namespace: example-namespace
    spec:
    podSelector: {}
    policyTypes:
    - Ingress
    ingress:
    - from:
    - podSelector: {}
    EOF

    kubectl apply -f allow-same-namespace-example-namespace.yaml
  5. Repeat steps 2–4 for each namespace reported in step 1, adjusting the namespace: field and filenames accordingly.

  6. Verification (run on any machine with kubectl access):

    (kubectl get ns -o json; kubectl get networkpolicy -A -o json) \
    | jq -rs '
    (.[0].items | map(.metadata.name)
    | map(select(.!="kube-system" and .!="kube-public" and .!="kube-node-lease"))) as $ns
    |
    ( (.[1].items // [])
    | sort_by(.metadata.namespace)
    | group_by(.metadata.namespace)
    | map({key: .[0].metadata.namespace, value: length})
    | from_entries
    ) as $np
    |
    [ $ns[] | select( ($np[.] // 0) == 0 ) ] as $missing
    |
    if ($missing|length)>0
    then ($missing[] | "FOUND_NAMESPACE_WITHOUT_NETWORKPOLICY:"+.)
    else "ALL_NAMESPACES_HAVE_NETWORK_POLICIES"
    end
    '

    Confirm the output is:

    "ALL_NAMESPACES_HAVE_NETWORK_POLICIES"
Using kubectl
# 1) Identify namespaces missing NetworkPolicies
# Run on: any machine with kubectl access
(kubectl get ns -o json; kubectl get networkpolicy -A -o json) \
| jq -rs '
(.[0].items | map(.metadata.name)
| map(select(.!="kube-system" and .!="kube-public" and .!="kube-node-lease"))) as $ns
|
( (.[1].items // [])
| sort_by(.metadata.namespace)
| group_by(.metadata.namespace)
| map({key: .[0].metadata.namespace, value: length})
| from_entries
) as $np
|
[ $ns[] | select( ($np[.] // 0) == 0 ) ] as $missing
|
if ($missing|length)>0
then ($missing[] | "FOUND_NAMESPACE_WITHOUT_NETWORKPOLICY:"+.)
else "ALL_NAMESPACES_HAVE_NETWORK_POLICIES"
end
'

Create a baseline “default-deny ingress” NetworkPolicy for each application namespace that is missing one. Adjust namespaces and allowed traffic as appropriate for your environment.

Example: if the audit output contains:

  • FOUND_NAMESPACE_WITHOUT_NETWORKPOLICY:dev
  • FOUND_NAMESPACE_WITHOUT_NETWORKPOLICY:prod

create policies like below.

# 2) Create a default-deny ingress NetworkPolicy for namespace 'dev'
# Run on: any machine with kubectl access
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: dev
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []
EOF

# 3) Create a default-deny ingress NetworkPolicy for namespace 'prod'
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: prod
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []
EOF

If you want to allow intra-namespace traffic but still isolate from other namespaces, use this pattern instead:

# Example allow-same-namespace ingress for namespace 'dev'
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace-ingress
namespace: dev
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}
EOF

Repeat for each non-excluded namespace reported by the audit until all have at least one appropriate NetworkPolicy.

# 4) Verification: confirm all non-system namespaces now have NetworkPolicies
# Run on: any machine with kubectl access
(kubectl get ns -o json; kubectl get networkpolicy -A -o json) \
| jq -rs '
(.[0].items | map(.metadata.name)
| map(select(.!="kube-system" and .!="kube-public" and .!="kube-node-lease"))) as $ns
|
( (.[1].items // [])
| sort_by(.metadata.namespace)
| group_by(.metadata.namespace)
| map({key: .[0].metadata.namespace, value: length})
| from_entries
) as $np
|
[ $ns[] | select( ($np[.] // 0) == 0 ) ] as $missing
|
if ($missing|length)>0
then ($missing[] | "FOUND_NAMESPACE_WITHOUT_NETWORKPOLICY:"+.)
else "ALL_NAMESPACES_HAVE_NETWORK_POLICIES"
end
'
Automation
#!/usr/bin/env bash
set -euo pipefail

# This script:
# - Detects namespaces without any NetworkPolicy (excluding kube-system, kube-public, kube-node-lease)
# - Creates a default deny-all ingress/egress NetworkPolicy per such namespace
# - Is safe to re-run (it will not overwrite an existing policy)
# - Verifies that all namespaces now have at least one NetworkPolicy

# Run from any machine with kubectl access and the desired context set.

# 1. Detect namespaces missing NetworkPolicies
echo "[*] Detecting namespaces without any NetworkPolicy..."

missing_ns=$(
(kubectl get ns -o json; kubectl get networkpolicy -A -o json) \
| jq -rs '
(.[0].items | map(.metadata.name)
| map(select(.!="kube-system" and .!="kube-public" and .!="kube-node-lease"))) as $ns
|
( (.[1].items // [])
| sort_by(.metadata.namespace)
| group_by(.metadata.namespace)
| map({key: .[0].metadata.namespace, value: length})
| from_entries
) as $np
|
[ $ns[] | select( ($np[.] // 0) == 0 ) ] as $missing
|
$missing[]
' 2>/dev/null || true
)

if [[ -z "${missing_ns}" ]]; then
echo "[*] ALL_NAMESPACES_HAVE_NETWORK_POLICIES (no changes required)"
else
echo "[*] Namespaces without NetworkPolicy:"
printf ' - %s\n' ${missing_ns}

# 2. Create a default deny-all NetworkPolicy in each missing namespace
# This follows the benchmark remediation: create NetworkPolicy objects as needed.
# You may later refine these policies to meet your application's needs.
echo "[*] Creating default deny-all NetworkPolicy in each missing namespace..."

# Template for the policy (namespaced via kubectl -n)
read -r -d '' NP_TEMPLATE <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
EOF

for ns in ${missing_ns}; do
echo " -> Processing namespace: ${ns}"

# Idempotency safeguard: if *any* NetworkPolicy already exists (race condition), skip
if kubectl get networkpolicy -n "${ns}" >/dev/null 2>&1; then
echo " Skipping: namespace ${ns} now has at least one NetworkPolicy"
continue
fi

# Create the default deny-all NetworkPolicy if it does not exist
if ! kubectl get networkpolicy default-deny-all -n "${ns}" >/dev/null 2>&1; then
echo " Creating NetworkPolicy default-deny-all in ${ns}"
printf '%s\n' "${NP_TEMPLATE}" | kubectl apply -n "${ns}" -f -
else
echo " NetworkPolicy default-deny-all already exists in ${ns}, skipping"
fi
done
fi

# 3. Verification: re-run the audit to ensure compliance
echo "[*] Verifying that all namespaces now have at least one NetworkPolicy..."

(kubectl get ns -o json; kubectl get networkpolicy -A -o json) \
| jq -rs '
(.[0].items | map(.metadata.name)
| map(select(.!="kube-system" and .!="kube-public" and .!="kube-node-lease"))) as $ns
|
( (.[1].items // [])
| sort_by(.metadata.namespace)
| group_by(.metadata.namespace)
| map({key: .[0].metadata.namespace, value: length})
| from_entries
) as $np
|
[ $ns[] | select( ($np[.] // 0) == 0 ) ] as $missing
|
if ($missing|length)>0
then ($missing[] | "FOUND_NAMESPACE_WITHOUT_NETWORKPOLICY:"+.)
else "ALL_NAMESPACES_HAVE_NETWORK_POLICIES"
end
'

Additional Reading: