Skip to main content

Minimize Admission Of Containers With NET_RAW Capability

More Info:

Do not generally permit containers with the potentially dangerous NET_RAW capability.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. Identify namespaces with user workloads and existing Pod Security controls

    • Run on: any machine with kubectl access
    kubectl get ns
    kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.labels}{"\n"}{end}'
    kubectl get psp 2>/dev/null || echo "No PodSecurityPolicies found"
    kubectl get validatingwebhookconfigurations.admissionregistration.k8s.io
    kubectl get mutatingwebhookconfigurations.admissionregistration.k8s.io
    • Decide which namespaces are for user workloads and require explicit restriction.
  2. Review current use of NET_RAW in existing workloads

    • Run on: any machine with kubectl access
    # List pods that explicitly add NET_RAW
    kubectl get pods --all-namespaces -o json \
    | jq -r '
    .items[] |
    .metadata.namespace as $ns |
    .metadata.name as $pod |
    (.spec.containers[]?, .spec.initContainers[]?) as $c |
    ($c.securityContext.capabilities.add[]? // empty) as $cap |
    select($cap == "NET_RAW") |
    [$ns, $pod, $c.name, $cap] | @tsv
    '

    # List pods that drop all capabilities (good baseline)
    kubectl get pods --all-namespaces -o json \
    | jq -r '
    .items[] |
    .metadata.namespace as $ns |
    .metadata.name as $pod |
    (.spec.containers[]?, .spec.initContainers[]?) as $c |
    select($c.securityContext.capabilities.drop[]? == "ALL") |
    [$ns, $pod, $c.name] | @tsv
    '
    • For each workload using NET_RAW, confirm whether it is strictly required for functionality.
  3. Decide the policy mechanism to enforce NET_RAW restriction per namespace

    • If using built-in Pod Security Admission (PSA):
      kubectl get ns --show-labels
      Decide whether to move namespaces to restricted or keep baseline and add an admission policy for NET_RAW.
    • If using OPA Gatekeeper or Kyverno, decide which policy engine to use or extend.
  4. Review or define namespace-level policies that deny NET_RAW

    • Example Gatekeeper audit (if Gatekeeper installed):
      kubectl get k8sallowedcapabilities.constraints.gatekeeper.sh -A
      kubectl get k8spspallowedcapabilities.constraints.gatekeeper.sh -A
    • Example Kyverno audit (if Kyverno installed):
      kubectl get clusterpolicy,policy -A
    • For each user-workload namespace, ensure there is a policy that either forbids NET_RAW entirely or allows it only for explicitly named workloads. If missing, plan to create such a policy (e.g., Gatekeeper Constraint or Kyverno Policy) targeting that namespace.
  5. Tighten or add securityContext in manifests for required exceptions

    • For workloads that legitimately need NET_RAW, explicitly declare and isolate the requirement in their manifests so policies can allow only those:
    # Example: inspect a specific deployment manifest
    kubectl get deploy -n <namespace> <deployment-name> -o yaml
    • Ensure manifests for workloads that do not need NET_RAW either omit capability additions or explicitly drop all capabilities:
    securityContext:
    allowPrivilegeEscalation: false
    capabilities:
    drop: ["ALL"]
  6. Verify that admission is now minimized for NET_RAW

    • After updating policies and manifests, test admission in a user-workload namespace that should be restricted:
    cat <<'EOF' | kubectl apply -n <user-namespace> -f -
    apiVersion: v1
    kind: Pod
    metadata:
    name: net-raw-test-deny
    spec:
    containers:
    - name: c
    image: busybox
    command: ["sleep", "3600"]
    securityContext:
    capabilities:
    add: ["NET_RAW"]
    EOF
    • Confirm that this pod is rejected with an admission error referring to the policy (or, if allowed by design in a specific exception namespace, confirm that it is the only such namespace and that others are denied).
Using kubectl
# 1. List all namespaces so you can decide which host user workloads
# Run on: any machine with kubectl access
kubectl get ns -o name

A problem exists if you find namespaces that run user workloads but have no policy objects controlling capabilities (next steps).


# 2. For each user-workload namespace, list PodSecurityPolicies (if used)
# Replace NAMESPACE with the target namespace
kubectl get podsecuritypolicies.policy -o yaml

Problem indicators:

  • PSPs exist that are referenced by your workloads’ ServiceAccounts (RBAC bindings), and:
    • .spec.allowedCapabilities includes NET_RAW, or
    • .spec.defaultAddCapabilities includes NET_RAW, or
    • .spec.requiredDropCapabilities does not include NET_RAW while other caps are restricted
  • Or: there is no PSP at all in a cluster where PSP is the intended control.

# 3. For each user-workload namespace, list local security policies
# Run on: any machine with kubectl access

# Pod Security admission labels on the namespace
kubectl get ns NAMESPACE -o yaml

# PodSecurityPolicy (legacy) references via RBAC (optional deeper check)
kubectl get role,rolebinding,clusterrole,clusterrolebinding -n NAMESPACE -o yaml

Problem indicators:

  • Namespace has no Pod Security admission labels (e.g. pod-security.kubernetes.io/enforce), so you rely only on ad‑hoc controls.
  • RBAC bindings grant access to permissive PSPs (from previous step) that allow NET_RAW.

# 4. Inspect PodSpecs in the namespace for explicit NET_RAW use
# Run on: any machine with kubectl access

# Current running Pods
kubectl get pods -n NAMESPACE -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{range .spec.containers[*]} {.name}{" capsAdd:"}{.securityContext.capabilities.add}{" capsDrop:"}{.securityContext.capabilities.drop}{"\n"}{end}{"\n"}{end}'

# All workload controllers (Deployments, DaemonSets, etc.)
kubectl get deploy,sts,ds,job,cronjob -n NAMESPACE -o yaml

Problem indicators:

  • Any container shows capsAdd:[NET_RAW ...] or similar in .securityContext.capabilities.add.
  • Workload manifests define securityContext.capabilities.add including NET_RAW, or fail to drop it when your policy model expects explicit dropping.

# 5. Check for namespace-level defaulting via mutating/validating webhooks
# Run on: any machine with kubectl access
kubectl get mutatingwebhookconfigurations,validatingwebhookconfigurations -o yaml

Problem indicators:

  • Webhooks that add NET_RAW to container capabilities.
  • Absence of any validating webhook that enforces dropping NET_RAW when your security model expects such enforcement.

# 6. If using Pod Security Admission (PSA), verify the effective level
# Run on: any machine with kubectl access
kubectl label ns NAMESPACE --list

Problem indicators:

  • Namespace labels such as:
    • pod-security.kubernetes.io/enforce=privileged
    • or no pod-security.kubernetes.io/* labels at all
  • In these cases, containers may run with NET_RAW unless controlled by other mechanisms.

# 7. Spot-check a known sensitive namespace for an example Pod
# Run on: any machine with kubectl access
kubectl get pod PODNAME -n NAMESPACE -o yaml

Problem indicators:

  • Under spec.containers[].securityContext.capabilities.add, NET_RAW is present without a strong, documented business justification.
  • There is no organizational pattern (annotations, labels, documentation) explaining why NET_RAW is required.
Automation
#!/usr/bin/env bash
set -euo pipefail

# This script inspects all namespaces for policies that restrict the NET_RAW capability.
# It does NOT change anything; it only reports for review.

echo "=== Cluster-wide NET_RAW admission review ==="
echo

echo "1) Namespaces and PodSecurity admission levels (if enabled)"
echo "----------------------------------------------------------"
kubectl get ns -o json | jq -r '
.items[]
| .metadata as $m
| (
$m.labels."pod-security.kubernetes.io/enforce",
$m.labels."pod-security.kubernetes.io/warn",
$m.labels."pod-security.kubernetes.io/audit"
) as $ps
| [
$m.name,
$m.labels."pod-security.kubernetes.io/enforce",
$m.labels."pod-security.kubernetes.io/enforce-version",
$m.labels."pod-security.kubernetes.io/warn",
$m.labels."pod-security.kubernetes.io/warn-version",
$m.labels."pod-security.kubernetes.io/audit",
$m.labels."pod-security.kubernetes.io/audit-version"
]
| @tsv' \
| awk 'BEGIN {
OFS="\t";
print "NAMESPACE","ENFORCE","ENFORCE_VER","WARN","WARN_VER","AUDIT","AUDIT_VER"
} {print}'

echo
echo "Problem indication for step 1:"
echo "- Namespaces running user workloads that are NOT labeled with at least ENFORCE=baseline or ENFORCE=restricted"
echo " may allow NET_RAW unless another policy (PSP/Gatekeeper/PodSecurityPolicy replacement) blocks it."
echo

echo "2) PodSecurityPolicies (if still present) and their NET_RAW rules"
echo "-----------------------------------------------------------------"
if kubectl api-resources | grep -q "^podsecuritypolicies.extensions" || \
kubectl api-resources | grep -q "^podsecuritypolicies.policy"; then
kubectl get podsecuritypolicies -o json | jq -r '
.items[]
| .metadata.name as $name
| .spec as $s
| $s.requiredDropCapabilities as $reqDrop
| $s.allowedCapabilities as $allowed
| $s.defaultAddCapabilities as $defaultAdd
| [
$name,
(if ($reqDrop // []) | index("NET_RAW") then "YES" else "NO" end),
(if ($allowed // []) | index("NET_RAW") then "YES" else "NO" end),
(if ($defaultAdd // []) | index("NET_RAW") then "YES" else "NO" end)
]
| @tsv' \
| awk 'BEGIN {
OFS="\t";
print "PSP","REQUIRES_DROP_NET_RAW","ALLOWS_NET_RAW","DEFAULT_ADDS_NET_RAW"
} {print}'
else
echo "No PodSecurityPolicy API detected in this cluster."
fi

echo
echo "Problem indication for step 2:"
echo "- Any PSP that:"
echo " * does NOT require dropping NET_RAW (REQUIRES_DROP_NET_RAW=NO) AND"
echo " * either ALLOWS_NET_RAW=YES or DEFAULT_ADDS_NET_RAW=YES"
echo " indicates a risk that containers can keep or gain NET_RAW."
echo

echo "3) Gatekeeper/OPA or Kyverno-like policies mentioning NET_RAW (if installed)"
echo "---------------------------------------------------------------------------"
echo "# Gatekeeper/OPA constraints and templates referencing NET_RAW:"
if kubectl api-resources | grep -qi 'constrainttemplate'; then
kubectl get constrainttemplates -A -o yaml | grep -n --color=always -i 'NET_RAW' || \
echo "No NET_RAW references found in ConstraintTemplates."
echo
kubectl get constraints -A -o yaml 2>/dev/null | grep -n --color=always -i 'NET_RAW' || \
echo "No NET_RAW references found in Constraints."
else
echo "No Gatekeeper ConstraintTemplate API detected."
fi

echo
echo "# Kyverno ClusterPolicies/Policies referencing NET_RAW:"
if kubectl api-resources | grep -qi '^clusterpolicies.kyverno.io'; then
kubectl get clusterpolicies -A -o yaml | grep -n --color=always -i 'NET_RAW' || \
echo "No NET_RAW references found in Kyverno ClusterPolicies."
fi
if kubectl api-resources | grep -qi '^policies.kyverno.io'; then
kubectl get policies -A -o yaml | grep -n --color=always -i 'NET_RAW' || \
echo "No NET_RAW references found in Kyverno Policies."
fi

echo
echo "Problem indication for step 3:"
echo "- Absence of any admission policies (Gatekeeper/Kyverno or similar) that:"
echo " * explicitly require dropping NET_RAW, or"
echo " * forbid adding NET_RAW,"
echo " in namespaces without strict PodSecurity admission."
echo

echo "4) Workload specs that currently request or retain NET_RAW"
echo "---------------------------------------------------------"
echo "Scanning Pods, Deployments, StatefulSets, DaemonSets, Jobs, and CronJobs for NET_RAW..."
echo

resources=(
"pods"
"deployments.apps"
"statefulsets.apps"
"daemonsets.apps"
"jobs.batch"
"cronjobs.batch"
)

for r in "${resources[@]}"; do
echo "== $r =="
if ! kubectl get "$r" --all-namespaces >/dev/null 2>&1; then
echo " (resource type not found or not used)"
echo
continue
fi

kubectl get "$r" --all-namespaces -o json | jq -r '
.items[]
| .metadata as $m
| .spec as $s
| (
if .kind=="Pod" then $s
else
# handle controllers with pod templates
( if $s.template then $s.template.spec else $s end )
end
) as $pspec
| ($pspec.containers // []) as $containers
| ($pspec.initContainers // []) as $initContainers
| ($pspec.ephemeralContainers // []) as $ephContainers
| [$containers[], $initContainers[], $ephContainers[]]?
| select(. != null)
| .name as $cname
| .securityContext as $csec
| $pspec.securityContext as $psec
| [
$m.namespace,
$m.name,
.image,
$cname,
(
if ($csec.capabilities.drop // []) | index("NET_RAW")
then "YES"
else "NO"
end
),
(
if ($csec.capabilities.add // []) | index("NET_RAW")
then "YES"
else "NO"
end
),
(
if ($psec.capabilities.drop // []) | index("NET_RAW")
then "YES"
else "NO"
end
),
(
if ($psec.capabilities.add // []) | index("NET_RAW")
then "YES"
else "NO"
end
)
]
| @tsv' 2>/dev/null \
| awk 'BEGIN {
OFS="\t";
print "NAMESPACE","WORKLOAD_NAME","IMAGE","CONTAINER",
"CONTAINER_DROPS_NET_RAW","CONTAINER_ADDS_NET_RAW",
"POD_DROPS_NET_RAW","POD_ADDS_NET_RAW"
} {print}'
echo
done

echo "Problem indication for step 4:"
echo "- Any line where:"
echo " * CONTAINER_ADDS_NET_RAW=YES or POD_ADDS_NET_RAW=YES -> container explicitly gains NET_RAW."
echo " * CONTAINER_DROPS_NET_RAW=NO and POD_DROPS_NET_RAW=NO -> no explicit drop;"
echo " combined with weak PodSecurity/admission policy this may allow NET_RAW to be retained."
echo
echo "Review guidance:"
echo "- Focus on user-workload namespaces that lack strict PodSecurity or other admission controls."
echo "- In those namespaces, identify workloads with ADDS_NET_RAW=YES or that do not drop NET_RAW."
echo "- Decide case-by-case whether NET_RAW is strictly required; if not, adjust manifests or add policies"
echo " (per-namespace admission controls) to require dropping NET_RAW or forbid adding it."

Additional Reading: