> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Containers Should Drop All Linux Capabilities

### More Info:

Verifies every container drops ALL capabilities and adds back only what it needs. Excess capabilities expand the attack surface of a compromised container.

### Risk Level

High

### Address

Security

### Compliance Standards

* Cloudanix Best Practice

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. Identify noncompliant pods (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl get pods --all-namespaces -o json | jq -r '
             [ .items[]
             | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
             | .metadata as $m
             | (($m.ownerReferences // [])[] | select(.controller) | .kind + "/" + $m.namespace + "/" + .name) // ("Pod/" + $m.namespace + "/" + $m.name)
             ] | unique[]'
           ```

        2. For a workload you control (example: Deployment in namespace “prod”), retrieve its manifest (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl -n prod get deployment my-app -o yaml > /tmp/my-app-deployment.yaml
           ```

        3. Edit the manifest to drop all capabilities and add back only what is needed (run on any machine with kubectl access):
           * Open the file:
             ```bash theme={null}
             nano /tmp/my-app-deployment.yaml
             ```
           * Under each `containers[]` and `initContainers[]` item, add or update:
             ```yaml theme={null}
             securityContext:
               capabilities:
                 drop:
                   - "ALL"
                 # add:
                 #   - "NET_BIND_SERVICE"
             ```
           * Remove any unneeded capabilities from `add:`; keep only strictly required ones.

        4. Apply the updated manifest (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl apply -f /tmp/my-app-deployment.yaml
           ```

        5. Repeat steps 2–4 for each noncompliant controller or standalone Pod you manage (e.g., `deployment`, `statefulset`, `daemonset`, `job`, `cronjob`, or `pod`).

        6. Verify all non-excluded namespaces are compliant (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl get pods --all-namespaces -o json | jq -r '
             [ .items[]
             | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
             | .metadata as $m
             | (.spec.nodeName // "") as $node
             | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
             | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
             | ((.spec.containers // []) + (.spec.initContainers // []))[]
             | (.securityContext.capabilities.drop // []) as $drop
             | (($drop | index("ALL")) or ($drop | index("all"))) as $ok
             | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
               + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
               + (if $node   == ""   then "" else " node=\($node)" end)
               + (if $labels == ""   then "" else " labels=\($labels)" end)
               + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
               + " container=\(.name) image=\(.image)"
               + " capabilitiesDrop=\(if ($drop | length) == 0 then "none" else ($drop | join("+")) end)"
               + " is_compliant=\(if $ok then "true" else "false" end)"
             ] as $rows
             | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
           ```
           Confirm remaining lines (if any) all show `is_compliant=true`.
      </Accordion>

      <Accordion title="Using kubectl">
        On any machine with kubectl access:

        1. Identify non‑compliant pods and their owning controllers

        ```bash theme={null}
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | ((.spec.containers // []) + (.spec.initContainers // []))[]
          | (.securityContext.capabilities.drop // []) as $drop
          | (($drop | index("ALL")) or ($drop | index("all"))) as $ok
          | select($ok | not)
          | "\($m.namespace) \($m.name) \($own.kind) \($own.name)"
          ] | unique[]' | column -t
        ```

        This lists: `NAMESPACE POD OWNER_KIND OWNER_NAME`. For entries where `OWNER_KIND` is empty, you must edit the Pod directly; otherwise edit the owning object (Deployment, StatefulSet, DaemonSet, Job, CronJob, etc.).

        2. Edit the owning controller manifest(s)

        For each non‑compliant controller (example: a Deployment in namespace `prod` named `web`):

        ```bash theme={null}
        kubectl -n prod edit deployment web
        ```

        Under every `spec.template.spec.containers[]` and `spec.template.spec.initContainers[]` entry, add:

        ```yaml theme={null}
        securityContext:
          capabilities:
            drop:
              - "ALL"
            # add:
            #   - "CAP_NEEDED_BY_APP"
        ```

        Maintain any existing `securityContext` fields; just merge the capabilities stanza. Save and exit to apply.

        3. Edit standalone Pods (no controller)

        For a Pod in namespace `default` named `test-pod` with no owner:

        ```bash theme={null}
        kubectl -n default edit pod test-pod
        ```

        Add the same block to each container and initContainer:

        ```yaml theme={null}
        securityContext:
          capabilities:
            drop:
              - "ALL"
            # add:
            #   - "CAP_NEEDED_BY_APP"
        ```

        Note: direct Pod edits are not persisted if something external (e.g., Helm, GitOps) recreates them; prefer fixing source manifests/IaC where applicable.

        4. Declarative example for future manifests

        When authoring or updating manifests, ensure each container includes:

        ```yaml theme={null}
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: example
          namespace: prod
        spec:
          replicas: 1
          selector:
            matchLabels:
              app: example
          template:
            metadata:
              labels:
                app: example
            spec:
              containers:
                - name: app
                  image: gcr.io/PROJECT/IMAGE:TAG
                  securityContext:
                    capabilities:
                      drop:
                        - "ALL"
                      # add:
                      #   - "CAP_NET_BIND_SERVICE"
        ```

        Apply with:

        ```bash theme={null}
        kubectl apply -f example-deployment.yaml
        ```

        5. Verification

        Run the benchmark audit command again from any machine with kubectl access:

        ```bash theme={null}
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | (.spec.nodeName // "") as $node
          | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
          | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | ((.spec.containers // []) + (.spec.initContainers // []))[]
          | (.securityContext.capabilities.drop // []) as $drop
          | (($drop | index("ALL")) or ($drop | index("all"))) as $ok
          | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
            + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
            + (if $node   == ""   then "" else " node=\($node)" end)
            + (if $labels == ""   then "" else " labels=\($labels)" end)
            + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
            + " container=\(.name) image=\(.image)"
            + " capabilitiesDrop=\(if ($drop | length) == 0 then "none" else ($drop | join("+")) end)"
            + " is_compliant=\(if $ok then "true" else "false" end)"
          ] as $rows
          | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
        ```

        Confirm output is `is_compliant=true` only, or that each listed container shows `is_compliant=true`.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Purpose: Ensure all non-system Pods' containers drop ALL Linux capabilities.
        # Platform: GKE (or any Kubernetes cluster accessible via current kube-context)
        # Requirements: kubectl, jq, sed; current kube-context must target the cluster.

        set -euo pipefail

        # Namespace exclusions (match audit command)
        EXCLUDED_NAMESPACES=("kube-system" "kube-public" "kube-node-lease")

        log() { printf '%s\n' "$*" >&2; }

        # Build jq filter to exclude system namespaces
        jq_ns_filter='["kube-system","kube-public","kube-node-lease"] as $e
          | select(.metadata.namespace as $n | $e | index($n) | not)'

        # 1. Discover non-compliant Pods (jsonPointer to each offending container)
        log "[INFO] Discovering non-compliant Pods/containers ..."
        NON_COMPLIANT_JSON=$(kubectl get pods --all-namespaces -o json | jq -c '
          .items[]
          | '"$jq_ns_filter"' 
          | . as $pod
          | ([($pod.spec.containers // [] | to_entries[] | . + {type:"containers"})]
             + [($pod.spec.initContainers // [] | to_entries[] | . + {type:"initContainers"})])[]
          | . as $c
          | ($pod.metadata.namespace // "") as $ns
          | ($pod.metadata.name // "") as $podName
          | ($c.type) as $ctype
          | ($c.key | tostring) as $idx
          | ($c.value.name // "") as $cname
          | ($c.value.securityContext.capabilities.drop // []) as $drop
          | (($drop | index("ALL")) or ($drop | index("all"))) as $ok
          | select($ok | not)
          | {
              namespace: $ns,
              pod: $podName,
              containerType: $ctype,
              containerIndex: $idx,
              containerName: $cname
            }
        ' || true)

        if [[ -z "$NON_COMPLIANT_JSON" ]]; then
          log "[INFO] No running Pods found or jq produced no output."
        fi

        if ! echo "$NON_COMPLIANT_JSON" | jq -e 'length' >/dev/null 2>&1; then
          # jq -c emitted multiple lines; that's fine.
          :
        fi

        # Aggregate into an array
        mapfile -t NON_COMPLIANT <<<"$(printf '%s\n' "$NON_COMPLIANT_JSON" | sed '/^\s*$/d')"

        if [[ ${#NON_COMPLIANT[@]} -eq 0 ]]; then
          log "[INFO] All checked containers already drop ALL capabilities. Nothing to change."
        else
          log "[INFO] Found ${#NON_COMPLIANT[@]} non-compliant container entries."
        fi

        # 2. For each unique (namespace, pod), patch the Pod template in its controller
        #    by setting drop: ["ALL"] on all containers of that Pod kind.
        #    WARNING: This edits workload manifests; review diffs in git/IaC after running.

        # Helper: deduplicate "owner kind/ns/name" triples per Pod using jq again
        UNIQUE_OWNERS=$(kubectl get pods --all-namespaces -o json | jq -r '
          .items[]
          | '"$jq_ns_filter"'
          | . as $pod
          | ([($pod.spec.containers // [])[]] + [($pod.spec.initContainers // [])[]]) as $cs
          | ($cs | map(.securityContext.capabilities.drop // []) | flatten) as $drops
          | (($drops | index("ALL")) or ($drops | index("all"))) as $ok
          | select($ok | not)
          | ($pod.metadata.ownerReferences // []) as $owners
          | ($owners | map(select(.controller)) | first) as $own
          | if $own == null then empty else
              "ns=\($pod.metadata.namespace) pod=\($pod.metadata.name) " +
              "ok=" + ($ok|tostring) + " " +
              "ownerKind=\($own.kind) ownerName=\($own.name) uid=\($own.uid)"
            end
        ' | sort -u)

        if [[ -z "$UNIQUE_OWNERS" ]]; then
          log "[INFO] Non-compliant Pods appear to be unmanaged (no controller)."
          log "[INFO] You must fix these Pod specs in their source manifests or Helm charts manually."
        fi

        log "[INFO] Patching controllers to enforce capabilities.drop=[\"ALL\"] where possible ..."

        # Function to generate a generic patch for Deployments/StatefulSets/DaemonSets/Jobs/CronJobs
        gen_patch() {
          cat <<'EOF'
        spec:
          template:
            spec:
              containers:
              - name: PLACEHOLDER
                securityContext:
                  capabilities:
                    drop:
                    - "ALL"
              initContainers:
              - name: PLACEHOLDER
                securityContext:
                  capabilities:
                    drop:
                    - "ALL"
        EOF
        }

        while read -r line; do
          [[ -z "$line" ]] && continue
          ns=$(sed -n 's/.*ns=\([^ ]*\).*/\1/p' <<<"$line")
          kind=$(sed -n 's/.*ownerKind=\([^ ]*\).*/\1/p' <<<"$line")
          name=$(sed -n 's/.*ownerName=\([^ ]*\).*/\1/p' <<<"$line")

          # Only handle common controller types via kubectl patch; others must be updated in source/IaC.
          case "$kind" in
            Deployment|StatefulSet|DaemonSet|Job|CronJob)
              log "[INFO] Patching $kind/$ns/$name ..."
              # Build a strategic merge patch that sets drop: ["ALL"] on all containers by name.
              # We first fetch container names from the controller template.
              tmpl_json=$(kubectl get "$kind" "$name" -n "$ns" -o json 2>/dev/null || true)
              if [[ -z "$tmpl_json" ]]; then
                log "[WARN] Could not fetch $kind/$ns/$name; skipping."
                continue
              fi

              c_names=$(echo "$tmpl_json" | jq -r '.spec.template.spec.containers[].name' 2>/dev/null || true)
              ic_names=$(echo "$tmpl_json" | jq -r '.spec.template.spec.initContainers[].name' 2>/dev/null || true)

              patch_yaml="spec:
          template:
            spec:
        "

              if [[ -n "$c_names" ]]; then
                patch_yaml+="      containers:
        "
                while read -r cn; do
                  [[ -z "$cn" ]] && continue
                  patch_yaml+="      - name: ${cn}
                securityContext:
                  capabilities:
                    drop:
                    - \"ALL\"
        "
                done <<<"$c_names"
              fi

              if [[ -n "$ic_names" ]]; then
                patch_yaml+="      initContainers:
        "
                while read -r icn; do
                  [[ -z "$icn" ]] && continue
                  patch_yaml+="      - name: ${icn}
                securityContext:
                  capabilities:
                    drop:
                    - \"ALL\"
        "
                done <<<"$ic_names"
              fi

              printf '%s\n' "$patch_yaml" | kubectl patch "$kind" "$name" -n "$ns" --type merge -p "$(cat)" >/dev/null
              ;;
            *)
              log "[WARN] Owner kind $kind for $ns/$name is not handled automatically. Update its manifest/IaC to add capabilities.drop: [\"ALL\"]."
              ;;
          esac
        done <<<"$UNIQUE_OWNERS"

        log "[INFO] Waiting for updated Pods to roll out ..."
        sleep 10

        # 3. Verification: re-run the audit with is_compliant summary
        log "[INFO] Verifying that all applicable containers drop ALL capabilities ..."
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | (.spec.nodeName // "") as $node
          | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
          | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | ((.spec.containers // []) + (.spec.initContainers // []))[]
          | (.securityContext.capabilities.drop // []) as $drop
          | (($drop | index("ALL")) or ($drop | index("all"))) as $ok
          | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
            + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
            + (if $node   == ""   then "" else " node=\($node)" end)
            + (if $labels == ""   then "" else " labels=\($labels)" end)
            + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
            + " container=\(.name) image=\(.image)"
            + " capabilitiesDrop=\(if ($drop | length) == 0 then "none" else ($drop | join("+")) end)"
            + " is_compliant=\(if $ok then "true" else "false" end)"
          ] as $rows
          | if ($rows | map(select(. | test("is_compliant=false$"))) | length) == 0
            then "is_compliant=true"
            else $rows[]
            end
        '
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
