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
Remediation
Manual Steps
-
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; docount=$(kubectl get networkpolicy -n "$ns" --no-headers 2>/dev/null | wc -l)if [ "$count" -eq 0 ]; then echo "$ns"; fidone)echo "$ns_without_np" -
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.
-
To apply a default deny all ingress and egress NetworkPolicy for one namespace (replace
TARGET_NAMESPACEwith the namespace name; run on any machine with kubectl access):kubectl apply -f - <<'EOF'apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: default-deny-allnamespace: TARGET_NAMESPACEspec:podSelector: {}policyTypes:- Ingress- EgressEOF -
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/v1kind: NetworkPolicymetadata:name: default-deny-ingressnamespace: TARGET_NAMESPACEspec:podSelector: {}policyTypes:- IngressEOF -
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/egressbased onfrom/to(e.g., namespaceSelector, podSelector, ipBlock) andports.
- Select specific pods via
- List pods and labels to plan selectors:
-
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; docount=$(kubectl get networkpolicy -n "$ns" --no-headers 2>/dev/null | wc -l)if [ "$count" -eq 0 ]; then echo "$ns"; fidone)if [ -z "$ns_without_np" ]; thenecho "ALL_NAMESPACES_HAVE_NETWORK_POLICIES"elseecho "NAMESPACES_WITHOUT_NETWORK_POLICIES: $ns_without_np"fi
Using kubectl
On any machine with kubectl access:
- 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"
- 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
- 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