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

# Prefer Using Secrets As Files Over Secrets As Environment Variables

### More Info:

Secrets exposed as environment variables are more easily leaked through logs and process inspection than secrets mounted as files. Prefer file mounts.

### 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. **Identify pods using Secret environment variables**
           * Run this on any machine with `kubectl` access:
             ```bash theme={null}
             kubectl get pods --all-namespaces -o json \
               | jq -r '.items[]
                 | select(.spec.containers[].env? // [] | map(select(.valueFrom.secretKeyRef?)) | length > 0
                          or .spec.containers[].envFrom? // [] | map(select(.secretRef?)) | length > 0)
                 | "\(.metadata.namespace) \(.metadata.name)"' | sort -u
             ```
           * Save the list for review.

        2. **Inspect pod specs and confirm how each Secret is used**
           * For each `NAMESPACE POD` from step 1, inspect full spec:
             ```bash theme={null}
             kubectl get pod POD -n NAMESPACE -o yaml
             ```
           * In `spec.containers[*].env` / `envFrom`, note:
             * Secret name and keys.
             * Whether the same Secret is already mounted as a volume.
             * Application expectations (e.g., variable names used by the app).

        3. **Decide whether each Secret can be consumed via file instead of env**
           * For each env-based Secret:
             * Review application code/config (and documentation) to see if it can read from a file path (config option, CLI flag, or code change).
             * If the app cannot be changed (e.g., third-party image requiring env vars), document this as an accepted exception with justification and skip to the next Secret.
             * If it can be changed, plan the mount path and per-key filenames (e.g., `/var/run/secrets/<secret-name>/<key>`).

        4. **Refactor manifests to mount Secrets as volumes instead of env vars**
           * On any machine with `kubectl` access, edit the workload manifests (Deployment/StatefulSet/DaemonSet/Job/CronJob, not the running Pod) to:
             * Add a Secret volume:
               ```yaml theme={null}
               spec:
                 template:
                   spec:
                     volumes:
                       - name: app-secret
                         secret:
                           secretName: SECRET_NAME
               ```
             * Mount it into each container:
               ```yaml theme={null}
               containers:
                 - name: app
                   volumeMounts:
                     - name: app-secret
                       mountPath: /var/run/secrets/app
                       readOnly: true
               ```
             * Remove the corresponding `env` / `envFrom` entries that reference the same Secret keys, and update container args/config to use file paths instead of env vars.
           * Apply the updated manifest:
             ```bash theme={null}
             kubectl apply -f UPDATED_MANIFEST.yaml
             ```

        5. **Roll out and validate application behavior**
           * Ensure updated workloads are running:
             ```bash theme={null}
             kubectl rollout status deploy/DEPLOYMENT_NAME -n NAMESPACE
             ```
           * Confirm the application reads secrets from files (e.g., health checks succeed, functional tests pass, or logs indicate successful secret loading).

        6. **Re-audit to confirm reduced use of Secret environment variables**
           * Re-run the detection from step 1:
             ```bash theme={null}
             kubectl get pods --all-namespaces -o json \
               | jq -r '.items[]
                 | select(.spec.containers[].env? // [] | map(select(.valueFrom.secretKeyRef?)) | length > 0
                          or .spec.containers[].envFrom? // [] | map(select(.secretRef?)) | length > 0)
                 | "\(.metadata.namespace) \(.metadata.name)"' | sort -u
             ```
           * Verify that only documented exceptions remain and that all other workloads now consume Secrets via mounted files.
      </Accordion>

      <Accordion title="Using kubectl">
        ```bash theme={null}
        # 1) List all namespaces
        # Run on: any machine with kubectl access
        kubectl get ns
        ```

        Review each relevant namespace (or all, if unsure):

        ```bash theme={null}
        # 2) List all Pods in a namespace, including their containers and env definitions (wide view)
        # Replace <namespace> with the namespace under review.
        kubectl get pods -n <namespace> -o yaml
        ```

        In the output, a Pod is potentially problematic if:

        * Under `spec.containers[].env[]` you see `valueFrom.secretKeyRef`, for example:
          ```yaml theme={null}
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password
          ```
        * Or under `spec.initContainers[].env[]` you see the same pattern.
        * Or `envFrom.secretRef` is used:
          ```yaml theme={null}
          envFrom:
            - secretRef:
                name: app-secrets
          ```

        These indicate Secrets are injected as environment variables.

        To focus specifically on Pods that use `secretKeyRef` in env or envFrom:

        ```bash theme={null}
        # 3) Grep for secretKeyRef and envFrom->secretRef in a namespace
        kubectl get pods -n <namespace> -o yaml | grep -nE 'secretKeyRef|secretRef'
        ```

        Any matches under `env:` or `envFrom:` signal Pods that should be reviewed for possible migration to file-based secret mounts.

        For a single Pod you are investigating:

        ```bash theme={null}
        # 4) Inspect one Pod in detail
        kubectl get pod <pod-name> -n <namespace> -o yaml
        ```

        Again, look specifically for:

        * `spec.containers[].env[].valueFrom.secretKeyRef`
        * `spec.containers[].envFrom[].secretRef`
        * `spec.initContainers[].env[].valueFrom.secretKeyRef`
        * `spec.initContainers[].envFrom[].secretRef`

        Presence of these fields means the Pod is using Secrets as environment variables and should be considered for refactoring so the application reads Secrets from mounted files instead.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Report Pods that consume Secrets via environment variables (directly or via envFrom)
        # Run on: any machine with kubectl access and jq installed

        set -euo pipefail

        # Namespace filter: set to "" to scan all namespaces, or to a specific namespace
        NAMESPACE_FILTER=""

        if [[ -n "$NAMESPACE_FILTER" ]]; then
          NS_ARGS=( -n "$NAMESPACE_FILTER" )
          echo "Scanning namespace: $NAMESPACE_FILTER" >&2
        else
          NS_ARGS=( -A )
          echo "Scanning all namespaces" >&2
        fi

        # Collect all Pods once as JSON
        pods_json="$(kubectl get pods "${NS_ARGS[@]}" -o json)"

        # 1) Pods using env[].valueFrom.secretKeyRef
        echo "=== Pods using Secrets via env[].valueFrom.secretKeyRef (per-container, per-env var) ==="
        echo "$pods_json" | jq -r '
          .items[]
          | .metadata as $m
          | .spec.containers[]? as $c
          | $c.env[]?
          | select(.valueFrom.secretKeyRef != null)
          | {
              namespace: $m.namespace,
              pod: $m.name,
              container: $c.name,
              env_var: .name,
              secret_name: .valueFrom.secretKeyRef.name,
              secret_key: .valueFrom.secretKeyRef.key
            }
          | [.namespace, .pod, .container, .env_var, .secret_name, .secret_key]
          | @tsv
        ' | awk 'BEGIN { OFS="\t"; print "NAMESPACE","POD","CONTAINER","ENV_VAR","SECRET_NAME","SECRET_KEY" } 1'

        echo
        # 2) Pods using envFrom[].secretRef
        echo "=== Pods using Secrets via envFrom[].secretRef (whole Secret as env vars) ==="
        echo "$pods_json" | jq -r '
          .items[]
          | .metadata as $m
          | .spec.containers[]? as $c
          | $c.envFrom[]?
          | select(.secretRef != null)
          | {
              namespace: $m.namespace,
              pod: $m.name,
              container: $c.name,
              secret_name: .secretRef.name
            }
          | [.namespace, .pod, .container, .secret_name]
          | @tsv
        ' | awk 'BEGIN { OFS="\t"; print "NAMESPACE","POD","CONTAINER","SECRET_NAME" } 1'

        echo
        # 3) Summary counts per namespace
        echo "=== Summary: count of Pods per namespace that use Secrets via env or envFrom ==="
        echo "$pods_json" | jq -r '
          .items[]
          | .metadata as $m
          | {
              namespace: $m.namespace,
              pod: $m.name,
              has_env_secret: (
                (.spec.containers[]?.env[]?.valueFrom.secretKeyRef // empty) as $x
                | ($x | length) >= 0
              ) and (
                (.spec.containers[]?.env[]?.valueFrom.secretKeyRef) != null
              ),
              has_envfrom_secret: (
                (.spec.containers[]?.envFrom[]?.secretRef // empty) as $y
                | ($y | length) >= 0
              ) and (
                (.spec.containers[]?.envFrom[]?.secretRef) != null
              )
            }
          | select(.has_env_secret or .has_envfrom_secret)
          | .namespace
        ' | sort | uniq -c | awk 'BEGIN { print "COUNT\tNAMESPACE" } { print $1 "\t" $2 }'
        ```

        **How to interpret the output**

        * Any line in the first table (`env[].valueFrom.secretKeyRef`) or second table (`envFrom[].secretRef`) indicates a Pod/container that is exposing a Secret via environment variables.
        * These Pods are candidates for remediation: refactor the application to read from Secrets mounted as files instead of env vars.
        * The summary section helps you see which namespaces have the largest number of such Pods so you can prioritize review.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
