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
Remediation
Manual Steps
-
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.
-
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/v1kind: NetworkPolicymetadata:name: default-deny-allnamespace: example-namespacespec:podSelector: {}policyTypes:- Ingress- EgressEOF -
Apply the NetworkPolicy for that namespace (run on any machine with kubectl access):
kubectl apply -f default-deny-all-example-namespace.yaml -
(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/v1kind: NetworkPolicymetadata:name: allow-same-namespacenamespace: example-namespacespec:podSelector: {}policyTypes:- Ingressingress:- from:- podSelector: {}EOFkubectl apply -f allow-same-namespace-example-namespace.yaml -
Repeat steps 2–4 for each namespace reported in step 1, adjusting the
namespace:field and filenames accordingly. -
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)>0then ($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:devFOUND_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
'