Ensure All Namespaces Have Network Policies Defined
More Info:
Namespaces without NetworkPolicy objects allow unrestricted pod-to-pod traffic. Every namespace holding user workloads should have at least one NetworkPolicy defined.
Risk Level
High
Address
Security
Compliance Standards
- CIS GKE
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
List namespaces missing NetworkPolicy objects
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[]'Save the output list of namespaces; you will create at least one NetworkPolicy in each.
-
For a selected namespace, create a baseline “default deny ingress” NetworkPolicy
Run on: any machine with kubectl access
ReplaceTARGET_NAMESPACEwith the namespace you are fixing (for each namespace, repeat with its name):cat > default-deny-ingress-TARGET_NAMESPACE.yaml << 'EOF'apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: default-deny-ingressnamespace: TARGET_NAMESPACEspec:podSelector: {}policyTypes:- IngressEOFsed -i 's/TARGET_NAMESPACE/your-namespace-name/g' default-deny-ingress-your-namespace-name.yamlkubectl apply -f default-deny-ingress-your-namespace-name.yaml -
(Optional but recommended) Add a default deny egress policy for the namespace
Run on: any machine with kubectl access
ReplaceTARGET_NAMESPACEwith the namespace name:cat > default-deny-egress-TARGET_NAMESPACE.yaml << 'EOF'apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: default-deny-egressnamespace: TARGET_NAMESPACEspec:podSelector: {}policyTypes:- EgressEOFsed -i 's/TARGET_NAMESPACE/your-namespace-name/g' default-deny-egress-your-namespace-name.yamlkubectl apply -f default-deny-egress-your-namespace-name.yaml -
(Optional) Create explicit allow policies to restore required traffic
Run on: any machine with kubectl access
Example: allow all pods in the same namespace to talk to each other (adapt as needed per application):cat > allow-same-namespace-TARGET_NAMESPACE.yaml << 'EOF'apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: allow-same-namespacenamespace: TARGET_NAMESPACEspec:podSelector: {}ingress:- from:- podSelector: {}policyTypes:- IngressEOFsed -i 's/TARGET_NAMESPACE/your-namespace-name/g' allow-same-namespace-your-namespace-name.yamlkubectl apply -f allow-same-namespace-your-namespace-name.yaml -
Repeat for all namespaces that were missing NetworkPolicies
Run on: any machine with kubectl access
For each namespace identified in step 1, create at least one NetworkPolicy (for example, the default deny policies plus any needed allow rules) by repeating steps 2–4 with the appropriate namespace name. -
Verify all namespaces now have at least one NetworkPolicy
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'The check is remediated when the command prints
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[] | .)
else empty
end
'
# Suppose the above prints a list like:
# default
# dev
# prod
# Replace this list explicitly in the manifest below.
Create a baseline, deny-all-ingress / allow-all-egress NetworkPolicy for each workload namespace:
# 2. Create a manifest with one NetworkPolicy per affected namespace
# Run on: any machine with kubectl access
cat > baseline-namespace-networkpolicies.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: default
spec:
podSelector: {} # select all Pods in this namespace
policyTypes:
- Ingress
ingress: [] # deny all ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: dev
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: prod
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []
EOF
Apply the NetworkPolicies:
# 3. Apply the NetworkPolicy resources
# Run on: any machine with kubectl access
kubectl apply -f baseline-namespace-networkpolicies.yaml
Verification (rerun the benchmark audit logic):
# 4. Verify all non-system namespaces now have at least one NetworkPolicy
# 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
# Automation for CIS GKE 4.3.2:
# Ensure each non-system namespace has at least one NetworkPolicy.
# Runs from: any machine with kubectl access and jq installed.
# CONFIG: label selector for namespaces to manage
NS_SELECTOR='!name in (kube-system,kube-public,kube-node-lease)'
# 1. Discover namespaces that currently have zero NetworkPolicies
echo "[INFO] Discovering namespaces without NetworkPolicy objects..."
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 ) ][]'
)
if [[ -z "${MISSING_NS}" ]]; then
echo "[INFO] ALL_NAMESPACES_HAVE_NETWORK_POLICIES (excluding kube-system, kube-public, kube-node-lease)"
exit 0
fi
echo "[INFO] Namespaces without NetworkPolicy:"
printf ' - %s\n' ${MISSING_NS}
# 2. For each missing namespace, create a baseline NetworkPolicy if none exists.
# This example creates a default-deny-all policy. Review before use.
# You can customize per namespace by editing the manifest generation below.
for NS in ${MISSING_NS}; do
echo "[INFO] Processing namespace: ${NS}"
# Double-check idempotency: skip if any NetworkPolicy now exists (race-safe).
NP_COUNT=$(kubectl get networkpolicy -n "${NS}" --no-headers 2>/dev/null | wc -l || true)
if [[ "${NP_COUNT}" -gt 0 ]]; then
echo "[INFO] Namespace ${NS} already has NetworkPolicy objects; skipping creation."
continue
fi
echo "[INFO] Creating baseline default-deny-all NetworkPolicy in namespace ${NS}"
# Apply a default-deny policy using kubectl apply (idempotent).
# Policy name is deterministic so re-runs are safe.
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: ${NS}
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
EOF
done
# 3. Verification using the authoritative audit logic
echo "[INFO] Verifying that all non-system 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
'