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

# Sensitive Values Should Not Be Passed As Literal Env Vars

### More Info:

Verifies secret-like env vars are not set as literal values. Literal values land in the pod manifest, logs and kubectl describe.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* Cloudanix Best Practice

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. List offending Pods and identify the owning workload (run on any machine with kubectl access):
           ```sh 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 // []))[]
               | .name as $c
               | (.env // [])[]
               | select((.value != null) and (.name | test("PASSWORD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|PRIVATE_KEY"; "i")))
               | "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=\($c) env=\(.name) is_compliant=false"
             ] as $rows
             | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
           ```
           From each line, note `ns=...`, `owner=...` (e.g., `Deployment/default/my-app/...`), `container=...`, and `env=...`.

        2. For each offending env var, create or update a Secret holding the sensitive value (run on any machine with kubectl access, per namespace). Replace placeholders with the real values:
           ```sh theme={null}
           kubectl -n default create secret generic my-app-secret \
             --from-literal=DB_PASSWORD='actual-password-value' \
             --dry-run=client -o yaml > /tmp/my-app-secret.yaml

           kubectl apply -f /tmp/my-app-secret.yaml
           ```
           Use one key per env var (e.g., `DB_PASSWORD`, `API_TOKEN`).

        3. Patch the owning workload manifest to use `valueFrom.secretKeyRef` instead of a literal `value:` (run on any machine with kubectl access). First, export the current manifest:
           ```sh theme={null}
           kubectl -n default get deployment my-app -o yaml > /tmp/my-app-deploy.yaml
           ```
           Then edit `/tmp/my-app-deploy.yaml`:
           * Find the container with the offending env, e.g.:
             ```yaml theme={null}
             env:
               - name: DB_PASSWORD
                 value: "actual-password-value"
             ```
           * Replace with:
             ```yaml theme={null}
             env:
               - name: DB_PASSWORD
                 valueFrom:
                   secretKeyRef:
                     name: my-app-secret
                     key: DB_PASSWORD
             ```
           * Ensure you do this for every env var flagged by the audit.

        4. Apply the updated workload manifest so new Pods use the Secret (run on any machine with kubectl access):
           ```sh theme={null}
           kubectl apply -f /tmp/my-app-deploy.yaml
           ```
           For controllers that don’t roll Pods automatically (e.g., some StatefulSets/Jobs), trigger a restart as appropriate, understanding this will restart the affected Pods.

        5. Confirm new Pods no longer contain literal sensitive values in their env definition (run on any machine with kubectl access):
           ```sh theme={null}
           kubectl -n default get pods -l app=my-app -o yaml | \
             grep -A2 "name: DB_PASSWORD"
           ```
           You should see only a `valueFrom: secretKeyRef:` block and no `value:` line for the sensitive variable names.

        6. Re-run the benchmark audit to verify compliance (run on any machine with kubectl access):
           ```sh 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 // []))[]
               | .name as $c
               | (.env // [])[]
               | select((.value != null) and (.name | test("PASSWORD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|PRIVATE_KEY"; "i")))
               | "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=\($c) env=\(.name) is_compliant=false"
             ] as $rows
             | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
           ```
           The cluster is compliant when the output shows `is_compliant=true` and no `is_compliant=false` rows.
      </Accordion>

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

        1. Identify the offending Pod and env var from the finding output, for example:
           * Namespace: `app-namespace`
           * Pod name: `web-7c9c6c7d4b-abcde`
           * Container: `web`
           * Env var: `DB_PASSWORD`

        2. Extract the current Pod spec (for reference only; do not apply this Pod directly because it is usually managed by a controller such as a Deployment):

        ```bash theme={null}
        kubectl get pod web-7c9c6c7d4b-abcde -n app-namespace -o yaml > /tmp/web-pod.yaml
        ```

        3. Determine the owning controller (e.g., Deployment) from the `owner=` field in the finding or via:

        ```bash theme={null}
        kubectl get pod web-7c9c6c7d4b-abcde -n app-namespace -o jsonpath='{.metadata.ownerReferences[0].kind}{" "}{.metadata.ownerReferences[0].name}{"\n"}'
        ```

        Assume the result is `Deployment web`.

        4. Create a Secret that will hold the sensitive value (if one does not already exist). Replace the literal with the real value:

        ```bash theme={null}
        kubectl -n app-namespace create secret generic db-credentials \
          --from-literal=DB_PASSWORD='REPLACE_WITH_REAL_PASSWORD'
        ```

        5. Patch the owning Deployment to reference the Secret via `valueFrom.secretKeyRef` instead of a literal `value:`. First, fetch the Deployment manifest:

        ```bash theme={null}
        kubectl get deployment web -n app-namespace -o yaml > /tmp/web-deploy.yaml
        ```

        6. Edit `/tmp/web-deploy.yaml`:
           * In the relevant container under `spec.template.spec.containers[]`, find the existing env entry:

        ```yaml theme={null}
                env:
                  - name: DB_PASSWORD
                    value: "REPLACE_WITH_REAL_PASSWORD"
        ```

        * Replace it with:

        ```yaml theme={null}
                env:
                  - name: DB_PASSWORD
                    valueFrom:
                      secretKeyRef:
                        name: db-credentials
                        key: DB_PASSWORD
        ```

        7. Apply the updated Deployment manifest:

        ```bash theme={null}
        kubectl apply -f /tmp/web-deploy.yaml
        ```

        This will recreate Pods managed by the Deployment with the Secret-based env var.

        8. Verification: rerun the audit (or a scoped variant) and confirm no `is_compliant=false` rows remain for this Pod/container/env:

        ```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 // []))[]
            | .name as $c
            | (.env // [])[]
            | select((.value != null) and (.name | test("PASSWORD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|PRIVATE_KEY"; "i")))
            | "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=\($c) env=\(.name) is_compliant=false"
          ] as $rows
          | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Fix CBP C4.1: Sensitive values should not be passed as literal env vars
        #
        # This script:
        #   - Scans all non-system namespaces for Pods with secret-like env var NAMES
        #     (PASSWORD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|PRIVATE_KEY) that have
        #     literal values set via `env[].value`.
        #   - Emits guidance and manifests you can use to convert them to Secret
        #     references (`valueFrom.secretKeyRef`).
        #
        # IMPORTANT:
        #   - There is no fully automatic, safe way to invent Secrets and wire them
        #     into apps without knowing the correct values and ownership model.
        #   - This script is NON-DESTRUCTIVE: it never deletes or patches live Pods.
        #   - Use the generated manifests and commands as a starting point, then
        #     review and apply manually.
        #
        # Requirements (run on any machine with kubectl access):
        #   - kubectl
        #   - jq
        #
        # Re-runnable: it only re-generates report files and guidance.

        set -euo pipefail

        WORKDIR="${PWD}/cbp-c4-1-remediation"
        REPORT="${WORKDIR}/literal-secret-envvars.jsonl"
        GUIDE="${WORKDIR}/literal-secret-envvars-remediation.txt"

        mkdir -p "${WORKDIR}"

        echo "Scanning cluster for Pods with sensitive literal env vars..."
        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 // []))[]
            | .name as $c
            | (.env // [])[]
            | select((.value != null)
                     and (.name | test("PASSWORD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|PRIVATE_KEY"; "i")))
            | {
                kind:       "Pod",
                namespace:  $m.namespace,
                pod:        $m.name,
                uid:        $m.uid,
                apiVersion: "v1",
                created:    ($m.creationTimestamp // ""),
                node:       $node,
                labels:     $labels,
                ownerKind:  ($own.kind // ""),
                ownerName:  ($own.name // ""),
                ownerUID:   ($own.uid // ""),
                container:  $c,
                envName:    .name,
                # DO NOT write the env value to disk, just record that it exists
                hasLiteralValue: true
              }
          ] as $rows
          | if ($rows | length) == 0
            then "[]"    # empty JSON array
            else $rows
            end
        ' > "${REPORT}"

        if [ "$(jq 'length' "${REPORT}")" -eq 0 ]; then
          echo "No Pods with sensitive literal env vars found. Cluster appears compliant."
          echo "is_compliant=true"
          exit 0
        fi

        echo "Found $(jq 'length' "${REPORT}") offending env vars. Writing remediation guidance to:"
        echo "  - ${REPORT} (structured JSONL-style array)"
        echo "  - ${GUIDE}  (human-readable guidance)"

        cat > "${GUIDE}" <<'EOF'
        CBP C4.1 REMEDIATION GUIDE
        ==========================

        Sensitive values must NOT be passed as literal env vars via `env[].value`.
        Instead, use:

          - `env[].valueFrom.secretKeyRef`  (recommended), or
          - a mounted Secret volume.

        For every row in literal-secret-envvars.jsonl:

        1. Identify the owning controller (Deployment/StatefulSet/Job/etc.) using:
           - fields ownerKind, ownerName, namespace in the JSON.

        2. On a machine with kubectl access, inspect the owning object, for example:
           - kubectl -n <namespace> get deployment <ownerName> -o yaml

        3. For each container/envName pair:
           a. Retrieve the CURRENT value from a live Pod (one-time, to seed a Secret):

                POD="<pod_name_from_report>"
                NS="<namespace_from_report>"
                kubectl -n "${NS}" get pod "${POD}" -o json \
                  | jq -r '
                      (.spec.containers[] + (.spec.initContainers // []))[]
                      | select(.name=="<container_from_report>")
                      | .env[]
                      | select(.name=="<envName_from_report>")
                      | .value
                    '

              NOTE: Run this only long enough to bootstrap the Secret, then delete
                    any local copies. Treat the output as sensitive.

           b. Create (or reuse) a Secret to hold this key. Example:

                NS="<namespace_from_report>"
                SECRET_NAME="<app>-sensitive-env"
                kubectl -n "${NS}" create secret generic "${SECRET_NAME}" \
                  --from-literal="<envName_from_report>=<VALUE_FROM_STEP_3A>"

              If the Secret already exists, update it instead:

                kubectl -n "${NS}" create secret generic "${SECRET_NAME}" \
                  --from-literal="<envName_from_report>=<VALUE_FROM_STEP_3A>" \
                  --dry-run=client -o yaml | kubectl apply -f -

           c. Edit the owning controller to replace `value:` with `valueFrom:`:

                kubectl -n <namespace_from_report> edit <ownerkind_from_report>/<ownerName_from_report>

              In the relevant container's env section, change:

                - name: <envName_from_report>
                  value: "<literal_value>"

              TO:

                - name: <envName_from_report>
                  valueFrom:
                    secretKeyRef:
                      name: <SECRET_NAME>
                      key: <envName_from_report>

              Save and exit. The controller will roll out new Pods using the Secret.

        4. Pods not managed by a controller (bare Pods):
           - Prefer to replace them with a Deployment/Job and follow the same pattern.
           - If you must keep a bare Pod, update its manifest in your IaC / manifests
             to use `valueFrom.secretKeyRef` and recreate the Pod.

        5. Post-change verification for each namespace:

           - Re-run the audit:

                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 // []))[]
                    | .name as $c
                    | (.env // [])[]
                    | select((.value != null)
                             and (.name | test("PASSWORD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|PRIVATE_KEY"; "i")))
                    | "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=\($c) env=\(.name) is_compliant=false"
                  ] as $rows
                  | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end
                '

           - You should ultimately see only:

                is_compliant=true

        EOF

        echo
        echo "Next steps:"
        echo "  1) Review ${REPORT} to see each violating Pod/container/envName."
        echo "       jq -C . ${REPORT} | less -R"
        echo "  2) Follow the step-by-step instructions in:"
        echo "       ${GUIDE}"
        echo
        echo "When you have updated the owning controllers to use valueFrom.secretKeyRef,"
        echo "re-run this script or the audit command to confirm you get: is_compliant=true"

        echo
        echo "Running verification audit now (read-only):"
        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 // []))[]
            | .name as $c
            | (.env // [])[]
            | select((.value != null)
                     and (.name | test("PASSWORD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|PRIVATE_KEY"; "i")))
            | "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=\($c) env=\(.name) is_compliant=false"
          ] as $rows
          | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end
        '
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
