> ## 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. On any machine with kubectl access, list offending Pods and pick one to fix:
           ```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.containers // []) + (.spec.initContainers // []))[]
               | .name as $c
               | (.env // [])[]
               | select((.value != null) and (.name | test("PASSWORD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|PRIVATE_KEY"; "i")))
               | "ns=\($m.namespace) pod=\($m.name) container=\($c) env=\(.name)"
             ] | unique[]'
           ```

        2. For each offending Pod, identify whether it is managed by a higher‑level controller (Deployment, StatefulSet, etc.) and capture that object for editing:
           ```bash theme={null}
           # Example for one offending pod; replace NAMESPACE and POD_NAME from step 1
           kubectl get pod POD_NAME -n NAMESPACE -o jsonpath='{.metadata.ownerReferences}' | jq

           # If owned by a Deployment (most common), get the Deployment manifest
           kubectl get deployment DEPLOYMENT_NAME -n NAMESPACE -o yaml > /tmp/deployment-NAMESPACE-DEPLOYMENT_NAME.yaml
           ```

        3. Create or update a Secret object that will hold the sensitive value, in the same namespace:
           ```bash theme={null}
           # Replace placeholders with your actual namespace/secret/key and value
           kubectl create secret generic app-secrets \
             -n NAMESPACE \
             --from-literal=DB_PASSWORD='ACTUAL_PASSWORD_VALUE' \
             --dry-run=client -o yaml > /tmp/app-secrets.yaml

           kubectl apply -f /tmp/app-secrets.yaml
           ```

        4. Edit the controller manifest to replace literal `value:` with `valueFrom.secretKeyRef` for each sensitive env var (do not edit Pods directly, as they will be recreated):
           ```bash theme={null}
           # Edit the saved Deployment manifest
           sed -i 's/DB_PASSWORD:.*/DB_PASSWORD:/g' /tmp/deployment-NAMESPACE-DEPLOYMENT_NAME.yaml  # optional cleanup

           # Open the file in an editor and, under spec.template.spec.containers[].env[], change, for example:
           # - name: DB_PASSWORD
           #   value: "ACTUAL_PASSWORD_VALUE"
           # to:
           # - name: DB_PASSWORD
           #   valueFrom:
           #     secretKeyRef:
           #       name: app-secrets
           #       key: DB_PASSWORD
           #
           # Repeat for all env vars whose names match PASSWORD|SECRET|TOKEN|APIKEY|API_KEY|CREDENTIAL|PRIVATE_KEY.

           kubectl apply -f /tmp/deployment-NAMESPACE-DEPLOYMENT_NAME.yaml
           ```

        5. Wait for the updated Pods to roll out and ensure the old ones are gone:
           ```bash theme={null}
           kubectl rollout status deployment/DEPLOYMENT_NAME -n NAMESPACE
           kubectl get pods -n NAMESPACE -o wide
           ```

        6. Verify the cluster is now compliant by rerunning the audit command from any machine with kubectl access; it should print `is_compliant=true` and no individual violations:
           ```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="Using kubectl">
        On any machine with kubectl access:

        1. Identify the offending Pod and env var

        ```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'
        ```

        Pick one violating Pod line and note: `ns`, `name`, `container`, and the `env` name.

        2. Export the owning workload manifest

        If the Pod is controlled by a Deployment (adjust kind if it’s a StatefulSet, DaemonSet, Job, etc.):

        ```bash theme={null}
        NAMESPACE="example-namespace"
        DEPLOYMENT="example-deployment"

        kubectl get deployment "${DEPLOYMENT}" -n "${NAMESPACE}" -o yaml > /tmp/deployment-secured.yaml
        ```

        3. Create a Secret to hold the sensitive value

        Replace `MY_SECRET_ENV`, `my-secret-name`, and `actual-secret-value` appropriately:

        ```bash theme={null}
        NAMESPACE="example-namespace"

        kubectl create secret generic my-secret-name \
          -n "${NAMESPACE}" \
          --from-literal=MY_SECRET_ENV=actual-secret-value
        ```

        4. Edit the manifest to use `valueFrom.secretKeyRef`

        Open `/tmp/deployment-secured.yaml` and in the relevant container’s `env` section, replace:

        ```yaml theme={null}
        env:
          - name: MY_SECRET_ENV
            value: "actual-secret-value"
        ```

        with:

        ```yaml theme={null}
        env:
          - name: MY_SECRET_ENV
            valueFrom:
              secretKeyRef:
                name: my-secret-name
                key: MY_SECRET_ENV
        ```

        Ensure indentation is valid and that you modify all containers / initContainers that used the literal value.

        5. Apply the updated manifest

        ```bash theme={null}
        kubectl apply -f /tmp/deployment-secured.yaml
        ```

        GKE will roll out new Pods with the Secret-based env var.

        6. Verification

        Re-run the benchmark audit command and confirm either `is_compliant=true` is printed or that the specific Pod/env no longer appears:

        ```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
        #
        # Automation: Replace sensitive literal env vars in Pods with Secret references
        #
        # WARNING:
        # - This script cannot safely modify existing running Pods without knowing where
        #   the secret values should come from or what Secret names/keys to use.
        # - It focuses on identifying all violations and generating manifest patches
        #   you can edit to wire to appropriate Secret objects.
        #
        # REQUIREMENTS:
        # - Run on any machine with kubectl access to the cluster and jq installed.
        # - kubectl current-context must point to the target GKE cluster.
        #
        # USAGE:
        #   1) Review dry-run output and generated patches.
        #   2) Create Secrets containing the sensitive data.
        #   3) Edit patches to reference those Secrets (valueFrom.secretKeyRef).
        #   4) Apply the patches.
        #   5) Re-run the verification step at the end of this script.
        #
        # This script is safe to re-run; it overwrites its own temp files.

        set -euo pipefail

        WORKDIR="./fix_sensitive_envvars_$(date +%Y%m%d_%H%M%S)"
        mkdir -p "${WORKDIR}/violations" "${WORKDIR}/patches"

        echo "Work directory: ${WORKDIR}"

        echo "Step 1: Detecting Pods with sensitive literal env vars (excluding kube-* system namespaces)..."
        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' \
          | tee "${WORKDIR}/violations/raw.txt"

        if grep -q '^is_compliant=true$' "${WORKDIR}/violations/raw.txt"; then
          echo "Cluster is already compliant; no literal sensitive env vars detected."
          exit 0
        fi

        echo "Step 2: Extracting unique violating Pod owner resources to patch (Deployments, StatefulSets, etc.)..."

        # Collect owner references (controllers) from the violation output.
        grep ' owner=' "${WORKDIR}/violations/raw.txt" | sed 's/.* owner=//' | awk '{print $1}' | sort -u > "${WORKDIR}/violations/owners.txt" || true

        if [[ ! -s "${WORKDIR}/violations/owners.txt" ]]; then
          echo "Violations found only on bare Pods (no controller)."
          echo "Manual remediation required: edit individual Pod manifests or (preferably) their higher-level workload definitions."
        else
          echo "Controllers with violations:"
          cat "${WORKDIR}/violations/owners.txt"
        fi

        echo "Step 3: Dumping controller manifests for review and patch preparation..."

        # For each controller (e.g., Deployment/ns/name/uid), dump the controller manifest.
        while read -r owner; do
          kind=$(echo "${owner}" | cut -d'/' -f1)
          ns=$(echo "${owner}"   | cut -d'/' -f2)
          name=$(echo "${owner}" | cut -d'/' -f3)

          # Map ReplicaSet/Job/DaemonSet to their editable top-level type when possible.
          # NOTE: We do not attempt complex owner resolution; we simply dump the resource by kind/ns/name.
          out_file="${WORKDIR}/violations/${kind}_${ns}_${name}.yaml"
          echo "  - Saving ${kind} ${ns}/${name} to ${out_file}"

          # If resource no longer exists, skip gracefully.
          if ! kubectl get "${kind}" "${name}" -n "${ns}" >/dev/null 2>&1; then
            echo "    WARNING: ${kind} ${ns}/${name} not found; it may have been deleted. Skipping."
            continue
          fi

          kubectl get "${kind}" "${name}" -n "${ns}" -o yaml > "${out_file}"
        done < "${WORKDIR}/violations/owners.txt"

        cat <<'EOF'

        Step 4: Prepare patches (manual editing required)

        For each saved manifest under violations/, locate containers or initContainers
        with env entries like:

          - name: DB_PASSWORD
            value: supersecret

        Change them to reference a Secret, e.g.:

          - name: DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: my-app-secrets
                key: db_password

        Or mount a Secret volume and consume via files instead.

        You can start patches from the full manifests you just saved. For example:

          cp violations/Deployment_default_myapp.yaml patches/Deployment_default_myapp_patch.yaml

        Then edit patches/Deployment_default_myapp_patch.yaml to:
          - Remove fields you do not want to change (keep metadata.name, metadata.namespace, and spec.template.* relevant to env vars).
          - Replace each sensitive "value:" with "valueFrom.secretKeyRef" as above.

        After editing, apply all patches:

          for f in patches/*.yaml; do
            echo "Applying patch $f"
            kubectl apply -f "$f"
          done

        EOF

        echo "Step 5: Verification command (run after you have applied your patches)"

        cat <<'EOF'
        To verify remediation, re-run the benchmark 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'

        Compliance is achieved when the output is exactly:

        is_compliant=true
        EOF
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
