> ## 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.

# Minimize Admission Of Containers Added Capabilities

### More Info:

Do not generally permit containers with capabilities assigned beyond the default set.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **List pods and identify containers with added capabilities**\
           Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl get pods --all-namespaces -o wide
           kubectl get pods --all-namespaces -o json \
             | jq -r '.items[]
               | .metadata as $m
               | .spec.containers[]
               | select(.securityContext.capabilities.add != null)
               | "\($m.namespace) \($m.name) \(.name) \(.securityContext.capabilities.add)"'
           ```
           Review each listed pod/container and confirm whether the added capabilities are truly required for the workload.

        2. **Review and decide on policy and workload changes**\
           Run on: any machine with kubectl access
           * Determine if the capability can be removed entirely from the workload spec.
           * If a capability is required, document the business/technical justification and ensure it is explicitly approved.
           * Decide if enforcement should be done via Pod Security Admission, PodSecurityPolicy (legacy), or another admission controller (e.g., OPA/Gatekeeper, Kyverno) to prevent future use of added capabilities.

        3. **Edit workloads to remove added capabilities**\
           Run on: any machine with kubectl access\
           For each non-exempt pod, edit its controller (Deployment/DaemonSet/StatefulSet/Job, etc.) and remove `add`ed capabilities from `securityContext.capabilities`:
           ```bash theme={null}
           # Example: editing a Deployment
           kubectl -n <namespace> edit deployment <deployment-name>
           ```
           In the editor, for each container, either:
           * Remove the entire `capabilities:` block if only `add` was used, or
           * Remove the `add:` field, leaving other fields (e.g., `drop:`) intact.\
             Save and exit to trigger a rollout and recreate pods without added capabilities.

        4. **Adjust or create admission policies to minimize allowedCapabilities**\
           Run on: any machine with kubectl access\
           If you use policy objects that support `allowedCapabilities` (e.g., PodSecurityPolicy, Gatekeeper/Kyverno policies), edit them so `allowedCapabilities` is either absent or an empty array:
           ```bash theme={null}
           # Example: editing a PodSecurityPolicy (if still in use)
           kubectl edit podsecuritypolicy <psp-name>
           ```
           In the YAML:
           ```yaml theme={null}
           allowedCapabilities: []
           ```
           or remove the `allowedCapabilities` field entirely.\
           For Gatekeeper/Kyverno, update the constraint/policy definitions similarly so they do not allow added capabilities except in explicitly justified cases.

        5. **Handle exceptions via dedicated namespaces or policies (if needed)**\
           Run on: any machine with kubectl access
           * For workloads that must retain specific capabilities, place them in dedicated namespaces and apply narrowly-scoped policies only there.
           * Ensure that cluster-wide or default policies do not include non-empty `allowedCapabilities`; instead, use per-namespace/per-workload exceptions with clear labels and documentation.

        6. **Verify no containers are running with added capabilities**\
           Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers \
           | while read -r pod_name pod_namespace; do
             kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json \
             | jq -c '.spec.containers[]' \
             | while read -r container; do
                 container_name=$(echo "${container}" | jq -r '.name')
                 container_caps_add=$(echo "${container}" | jq -r '.securityContext.capabilities.add' | sed -e 's/null/notset/g')
                 is_compliant=true
                 caps_list=""
                 if [ "${container_caps_add}" != "notset" ]; then
                   for cap in $(echo "${container_caps_add}" | jq -r '.[]'); do
                     caps_list+="${cap},"
                     is_compliant=false
                   done
                   caps_list=${caps_list%,}
                 fi
                 if [ "${is_compliant}" = false ]; then
                   echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} container_caps_add: ${caps_list} is_compliant: false"
                 fi
               done
           done
           ```
           Confirm that no lines with `is_compliant: false` remain, except for any explicitly approved, documented exceptions.
      </Accordion>

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

        This control is marked MANUAL: there is no single automatic fix, because legitimate workloads may require added Linux capabilities. Use these steps to review and, where appropriate, remove or tightly scope added capabilities.

        ### 1. Discover pods with added capabilities

        ```bash theme={null}
        kubectl get pods --all-namespaces -o wide
        ```

        Then use the supplied audit snippet (or a trimmed version) to list only non‑compliant containers:

        ```bash theme={null}
        kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | \
        while read -r pod_name pod_namespace; do
          kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | \
          jq -c '.spec.containers[]' | while read -r container; do
            container_name=$(echo "${container}" | jq -r '.name')
            container_caps_add=$(echo "${container}" | jq -r '.securityContext.capabilities.add' | sed -e 's/null/notset/g')
            is_compliant=true
            caps_list=""
            if [ "${container_caps_add}" != "notset" ]; then
              for cap in $(echo "${container_caps_add}" | jq -r '.[]'); do
                caps_list+="${cap},"
                is_compliant=false
              done
              caps_list=${caps_list%,}
            fi
            if [ "${is_compliant}" = false ]; then
              echo "NON-COMPLIANT pod=${pod_name} ns=${pod_namespace} container=${container_name} added_caps=${caps_list}"
            fi
          done
        done
        ```

        Use this output as your review list.

        ### 2. Review and decide per workload

        For each non‑compliant `pod / namespace / container`:

        1. Identify the owning resource:

           ```bash theme={null}
           kubectl get pod POD_NAME -n POD_NAMESPACE -o jsonpath='{.metadata.ownerReferences}' | jq
           ```

           Then inspect the owner (e.g., Deployment, DaemonSet, Job):

           ```bash theme={null}
           kubectl get deployment DEPLOYMENT_NAME -n POD_NAMESPACE -o yaml
           ```

        2. With the application owner, decide:
           * Are the added capabilities truly required?
           * Can they be removed entirely?
           * If not, can the set be reduced to the minimal necessary capabilities?

        Document the decision for each workload as part of your risk acceptance or exception process.

        ### 3. Remove or minimize added capabilities in manifests

        Edit the owning object manifests (Deployment, StatefulSet, DaemonSet, Job, CronJob, etc.) to remove `securityContext.capabilities.add` where possible.

        Example patterns:

        * **Current (non‑compliant)**

        ```yaml theme={null}
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: example
          namespace: example-ns
        spec:
          template:
            spec:
              containers:
                - name: app
                  image: your-image
                  securityContext:
                    capabilities:
                      add:
                        - NET_ADMIN
                        - SYS_TIME
        ```

        * **Preferred (no added capabilities)**

        ```yaml theme={null}
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: example
          namespace: example-ns
        spec:
          template:
            spec:
              containers:
                - name: app
                  image: your-image
                  securityContext:
                    capabilities: {}
        ```

        or simply omit the `securityContext.capabilities` stanza entirely from the container spec.

        Apply the change declaratively from any machine with `kubectl` access:

        ```bash theme={null}
        kubectl apply -f path/to/your-updated-manifest.yaml
        ```

        Repeat for each workload where you have determined that added capabilities are not strictly required.

        ### 4. (Optional) Review PodSecurityPolicy / PodSecurity / admission policies

        Where you still use policy objects that can specify `allowedCapabilities`, ensure they do not globally allow extra capabilities beyond the default set, unless set to an empty array as per the remediation.

        Examples:

        **PodSecurityPolicy (legacy clusters)**

        * Non‑compliant:

        ```yaml theme={null}
        apiVersion: policy/v1beta1
        kind: PodSecurityPolicy
        metadata:
          name: restricted
        spec:
          allowedCapabilities:
            - NET_ADMIN
            - SYS_ADMIN
        ```

        * Compliant:

        ```yaml theme={null}
        apiVersion: policy/v1beta1
        kind: PodSecurityPolicy
        metadata:
          name: restricted
        spec:
          allowedCapabilities: []
        ```

        Apply:

        ```bash theme={null}
        kubectl apply -f psp-restricted.yaml
        ```

        **Gatekeeper / Kyverno or similar admission policies**

        Review constraint or policy manifests and either remove `allowedCapabilities` or set them to `[]`, then:

        ```bash theme={null}
        kubectl apply -f path/to/policy.yaml
        ```

        ### 5. Verification

        After updating manifests and/or policies, re‑run the audit from any machine with `kubectl` access:

        ```bash theme={null}
        kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | \
        while read -r pod_name pod_namespace; do
          kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | \
          jq -c '.spec.containers[]' | while read -r container; do
            container_name=$(echo "${container}" | jq -r '.name')
            container_caps_add=$(echo "${container}" | jq -r '.securityContext.capabilities.add' | sed -e 's/null/notset/g')
            is_compliant=true
            if [ "${container_caps_add}" != "notset" ]; then
              is_compliant=false
            fi
            if [ "${is_compliant}" = false ]; then
              echo "NON-COMPLIANT pod=${pod_name} ns=${pod_namespace} container=${container_name} added_caps=${container_caps_add}"
            fi
          done
        done
        ```

        The cluster is aligned with the control when any remaining `NON-COMPLIANT` entries are understood, documented as exceptions, and justified as operationally required.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Purpose:
        #   Identify pods whose containers request added Linux capabilities and
        #   emit patched manifests (with added capabilities removed) that can be
        #   applied back to the cluster.
        #
        # Scope:
        #   Run on any machine with kubectl and jq installed and access to the cluster.
        #
        # Notes:
        #   - This does NOT mutate running pods directly; instead it generates
        #     corrected Pod/Workload manifests (YAML) you can review and apply.
        #   - This is idempotent: re-running will only regenerate patches for
        #     workloads that still add capabilities.
        #   - Because the benchmark control is MANUAL, you must review whether
        #     each capability is really needed before applying the change.
        #
        # Requirements:
        #   - kubectl configured with sufficient RBAC to list and get all resources.
        #   - jq installed and on PATH.

        set -euo pipefail

        OUT_DIR="${OUT_DIR:-./capability-fix-manifests}"
        mkdir -p "${OUT_DIR}"

        echo "Discovering pods with added capabilities (this may take some time)..."

        # Map: workload_kind/workload_namespace/workload_name -> has_added_caps=true
        declare -A WORKLOADS_WITH_CAPS

        # Helper: identify owning workload for a pod
        get_owner_ref() {
          local pod_json="$1"
          echo "${pod_json}" | jq -r '
            .metadata.ownerReferences // [] |
            map(select(.controller == true))[0] //
            {"kind":"Pod","name":.metadata.name,"apiVersion":.apiVersion} |
            "\(.kind),\(.name),\(.apiVersion)"
          '
        }

        # Scan all pods for added capabilities
        while IFS=$'\t' read -r pod_ns pod_name; do
          pod_json="$(kubectl get pod "${pod_name}" -n "${pod_ns}" -o json 2>/dev/null || true)"
          [ -z "${pod_json}" ] && continue

          # Check all containers in the pod for added capabilities
          has_caps="$(echo "${pod_json}" | jq -r '
            [
              (.spec.containers[]? | .securityContext.capabilities.add? // []),
              (.spec.initContainers[]? | .securityContext.capabilities.add? // [])
            ]
            | flatten
            | length > 0
          ')"

          if [ "${has_caps}" = "true" ]; then
            owner_ref="$(get_owner_ref "${pod_json}")"
            IFS=',' read -r owner_kind owner_name owner_api <<< "${owner_ref}"

            # Normalize owner kind/apiVersion into a resource type we can patch
            # Only handling common controllers; others you may patch manually.
            if [ "${owner_kind}" = "ReplicaSet" ]; then
              # Map ReplicaSet -> Deployment if possible
              rs_json="$(kubectl get rs "${owner_name}" -n "${pod_ns}" -o json 2>/dev/null || true)"
              if [ -n "${rs_json}" ]; then
                dep_owner="$(echo "${rs_json}" | jq -r '
                  .metadata.ownerReferences // [] |
                  map(select(.controller == true and .kind == "Deployment"))[0].name // ""
                ')"
                if [ -n "${dep_owner}" ]; then
                  key="Deployment/${pod_ns}/${dep_owner}"
                else
                  key="ReplicaSet/${pod_ns}/${owner_name}"
                fi
              else
                key="ReplicaSet/${pod_ns}/${owner_name}"
              fi
            elif [ "${owner_kind}" = "StatefulSet" ] || [ "${owner_kind}" = "DaemonSet" ] || [ "${owner_kind}" = "Job" ] || [ "${owner_kind}" = "CronJob" ]; then
              key="${owner_kind}/${pod_ns}/${owner_name}"
            else
              # Fall back to pod-level patching if no controller owner
              key="Pod/${pod_ns}/${pod_name}"
            fi

            WORKLOADS_WITH_CAPS["${key}"]=true
          fi
        done < <(kubectl get pods --all-namespaces -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name --no-headers)

        if [ "${#WORKLOADS_WITH_CAPS[@]}" -eq 0 ]; then
          echo "No pods found with added capabilities. Cluster is compliant for this control."
          exit 0
        fi

        echo "Identified ${#WORKLOADS_WITH_CAPS[@]} workload(s)/pod(s) with added capabilities."
        echo "Generating patched manifests in: ${OUT_DIR}"

        # Generate patched manifests with capabilities.add removed/emptied
        for key in "${!WORKLOADS_WITH_CAPS[@]}"; do
          IFS='/' read -r kind ns name <<< "${key}"
          echo "Processing ${kind} ${ns}/${name}"

          # Get original as YAML
          if ! kubectl get "${kind,,}.${kind,,}.k8s.io" "${name}" -n "${ns}" >/dev/null 2>&1; then
            # Fallback to short kind (e.g., deployment)
            res="${kind,,}"
          else
            res="${kind,,}.${kind,,}.k8s.io"
          fi

          if ! orig_yaml="$(kubectl get "${res}" "${name}" -n "${ns}" -o yaml 2>/dev/null)"; then
            # Try core resource (Pod)
            if ! orig_yaml="$(kubectl get "${kind,,}" "${name}" -n "${ns}" -o yaml 2>/dev/null)"; then
              echo "  WARNING: could not fetch ${kind} ${ns}/${name}; skipping"
              continue
            fi
          fi

          # Strip added capabilities from spec.template.spec.*containers for controllers,
          # or spec.containers/spec.initContainers for pods.
          patched_yaml="$(
            echo "${orig_yaml}" | \
            jq -r '
              . as $root
              | if .kind == "Pod" then
                  .spec.containers |=
                    map(
                      if .securityContext? and .securityContext.capabilities? then
                        .securityContext.capabilities.add = []
                      else
                        .
                      end
                    )
                  | .spec.initContainers |=
                    (map(
                      if .securityContext? and .securityContext.capabilities? then
                        .securityContext.capabilities.add = []
                      else
                        .
                      end
                    ) // .spec.initContainers)
                else
                  .spec.template.spec.containers |=
                    map(
                      if .securityContext? and .securityContext.capabilities? then
                        .securityContext.capabilities.add = []
                      else
                        .
                      end
                    )
                  | .spec.template.spec.initContainers |=
                    (map(
                      if .securityContext? and .securityContext.capabilities? then
                        .securityContext.capabilities.add = []
                      else
                        .
                      end
                    ) // .spec.template.spec.initContainers)
                end
              ' 2>/dev/null
          )"

          out_file="${OUT_DIR}/${kind}-${ns}-${name}.yaml"
          echo "${patched_yaml}" > "${out_file}"
          echo "  Wrote patched manifest: ${out_file}"
        done

        cat <<EOF

        Next steps (manual review required):

        1. Review each generated YAML file under:
           ${OUT_DIR}
           Ensure that removing added capabilities will not break workloads that
           legitimately require them.

        2. Apply the approved manifests back to the cluster, for example:
           for f in ${OUT_DIR}/*.yaml; do
             echo "Applying \$f"
             kubectl apply -f "\$f"
           done

        3. Verification (required by the benchmark):

           Re-run the audit command on any machine with kubectl access:

           kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace
           do
             kubectl get pod "\${pod_name}" --namespace "\${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container
             do
               container_name=\$(echo \${container} | jq -r '.name')
               container_caps_add=\$(echo \${container} | jq -r '.securityContext.capabilities.add' | sed -e 's/null/notset/g')
               is_compliant=true
               caps_list=""
               if [ "\${container_caps_add}" != "notset" ]; then
                 for cap in \$(echo "\${container_caps_add}" | jq -r '.[]'); do
                   caps_list+="\${cap},"
                   is_compliant=false
                 done
                 caps_list=\${caps_list%,}
               fi
               if [ "\${is_compliant}" = true ]; then
                 echo "***pod_name: \${pod_name} container_name: \${container_name} pod_namespace: \${pod_namespace} container_caps_add: \${container_caps_add} is_compliant: true"
               else
                 echo "***pod_name: \${pod_name} container_name: \${container_name} pod_namespace: \${pod_namespace} container_caps_add: \${caps_list} is_compliant: false"
               fi
             done
           done

           The cluster is compliant for this control when all lines show is_compliant: true.

        EOF
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://kubernetes.io/docs/concepts/security/pod-security-standards/](https://kubernetes.io/docs/concepts/security/pod-security-standards/)
