Skip to main content

Prefer Using Secrets Files Over Secrets Environment

More Info:

If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables.

Risk Level

High

Address

Security

Compliance Standards

  • APRA CPS 234 (Australia)
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS Critical Security Controls v8
  • CIS OKE
  • CMMC 2.0
  • CSA Cloud Controls Matrix v4
  • DPDPA
  • Digital Operational Resilience Act (EU)
  • Essential 8
  • ISO/IEC 27017
  • ISO/IEC 27018
  • ISO/IEC 27701
  • KSA PDPL
  • MAS Technology Risk Management (Singapore)
  • MITRE ATT&CK (Cloud)
  • NIST SP 800-171
  • NYDFS 23 NYCRR 500
  • SWIFT Customer Security Controls Framework
  • Sarbanes-Oxley IT General Controls
  • UK NCSC Cyber Assessment Framework

Triage and Remediation

Remediation

Manual Steps
  1. List workloads that reference Secrets in env vars

    • Run on: any machine with kubectl access
    • Command:
      kubectl get pods --all-namespaces -o json \
      | jq -r '
      .items[]
      | {ns: .metadata.namespace, pod: .metadata.name, containers: .spec.containers}
      | select(
      [.containers[].env[]? | select(.valueFrom.secretKeyRef!=null)] | length > 0
      )
      '
    • Purpose: Identify Pods whose containers consume Secrets via env.valueFrom.secretKeyRef.
  2. Identify which Secrets and keys are used as env vars

    • Run on: any machine with kubectl access
    • Command (tabular view):
      kubectl get pods --all-namespaces -o json \
      | jq -r '
      .items[]
      | .metadata.namespace as $ns
      | .metadata.name as $pod
      | .spec.containers[]
      | .name as $cname
      | .env[]?
      | select(.valueFrom.secretKeyRef!=null)
      | [$ns, $pod, $cname, .name, .valueFrom.secretKeyRef.name, .valueFrom.secretKeyRef.key]
      | @tsv
      ' \
      | column -t \
      | sed '1iNAMESPACE POD CONTAINER ENV_VAR SECRET_NAME SECRET_KEY'
    • Purpose: Build an inventory of env vars backed by Secrets to review with application owners.
  3. Review application capability to use mounted Secret files

    • Run: out-of-band review with application teams, using output from step 2
    • Questions to decide per env var:
      • Can the application be modified/configured to read this value from a file path instead of an environment variable?
      • Is the value only needed at startup (still better as file, but lower urgency) or at runtime (file strongly preferred)?
      • Are there existing Secret volumes or CSI Secret Store integrations that can be reused?
  4. Plan manifest changes to use Secret volumes instead of env vars

    • Run on: any machine with kubectl access
    • Action: For each container where the app can read from files:
      • Edit the Deployment/StatefulSet/Job manifest and:
        • Add a volumes entry referencing the Secret:
          volumes:
          - name: my-secret-vol
          secret:
          secretName: my-secret
        • Add a volumeMounts entry in the container:
          volumeMounts:
          - name: my-secret-vol
          mountPath: /var/run/secrets/my-secret
          readOnly: true
        • Remove the corresponding env entries that use valueFrom.secretKeyRef, and update application config to read from the mounted file path (e.g. /var/run/secrets/my-secret/<SECRET_KEY>).
  5. Apply and roll out updated manifests

    • Run on: any machine with kubectl access
    • Commands (example; adjust file/resource names):
      kubectl apply -f path/to/your-workload.yaml
      kubectl rollout status deployment/your-deployment -n your-namespace
    • Operational impact: Updating workload manifests will trigger rollouts; ensure you coordinate with app owners and have roll-back plans.
  6. Verify that Secrets are no longer exposed as env vars

    • Run on: any machine with kubectl access
    • Command:
      kubectl get pods --all-namespaces -o json \
      | jq -r '
      .items[]
      | {ns: .metadata.namespace, pod: .metadata.name, containers: .spec.containers}
      | select(
      [.containers[].env[]? | select(.valueFrom.secretKeyRef!=null)] | length > 0
      )
      '
    • Success criterion: For workloads you remediated, no Pods should appear in this output; remaining hits indicate env-based Secret usage that must be accepted by risk decision or further refactored.
Using kubectl

Using kubectl

1. List workloads that reference Secrets as environment variables

Run on: any machine with kubectl access.

kubectl get deploy,sts,ds,job,cronjob -A -o yaml | \
awk '
/kind: Deployment|kind: StatefulSet|kind: DaemonSet|kind: Job|kind: CronJob/ {kind=$2}
/name:/ && $1=="name:" {name=$2}
/namespace:/ {ns=$2}
/envFrom:/ {in_envfrom=1}
in_envfrom && /secretRef:/ {sr=1}
in_envfrom && sr && /name:/ && $1=="name:" {
printf "%s %s/%s uses secret %s via envFrom\n", kind, ns, name, $2
}
/env:/ {in_env=1}
in_env && /valueFrom:/ {vf=1}
in_env && vf && /secretKeyRef:/ {skr=1}
in_env && skr && /name:/ && $1=="name:" {
printf "%s %s/%s uses secret %s via env (secretKeyRef)\n", kind, ns, name, $2
}
'

What indicates a problem:

  • Lines reporting uses secret ... via envFrom or via env (secretKeyRef) mean that Secret data is injected into containers as environment variables.
  • These workloads should be reviewed to see whether they can instead consume the same secrets via a volume mount (files) and application code changes.

2. Inspect a specific workload’s environment configuration

Pick an item from the list above and inspect it in detail.

Deployment example:

kubectl get deployment -n <namespace> <name> -o yaml

Look for:

  • Under each container:
    • envFrom: with secretRef: (entire Secret exposed as env vars).
    • env: entries with valueFrom.secretKeyRef.

What indicates a problem:

  • Any envFrom.secretRef or env.valueFrom.secretKeyRef entries show that secrets are provided via environment variables.
  • Compare with volumeMounts: and volumes:: if there is no corresponding secret volume mount that the application could use instead, the pod is likely depending solely on env-based secrets.

3. List Secrets actually referenced by Pods

This helps correlate which Secret objects are used by which pods as env vars.

kubectl get pods -A -o yaml | \
awk '
/kind: Pod/ {kind=$2}
/name:/ && $1=="name:" {name=$2}
/namespace:/ {ns=$2}
/envFrom:/ {in_envfrom=1}
in_envfrom && /secretRef:/ {sr=1}
in_envfrom && sr && /name:/ && $1=="name:" {
printf "Pod %s/%s uses secret %s via envFrom\n", ns, name, $2
}
/env:/ {in_env=1}
in_env && /valueFrom:/ {vf=1}
in_env && vf && /secretKeyRef:/ {skr=1}
in_env && skr && /name:/ && $1=="name:" {
printf "Pod %s/%s uses secret %s via env (secretKeyRef)\n", ns, name, $2
}
'

What indicates a problem:

  • Any reported use of secret ... via envFrom or via env (secretKeyRef) is a candidate for refactoring to use mounted secret files instead of environment variables, subject to application and operational constraints.

4. Compare with Secret volume mounts (for context)

To see which pods already use Secrets as files:

kubectl get pods -A -o yaml | \
awk '
/kind: Pod/ {kind=$2}
/name:/ && $1=="name:" {name=$2}
/namespace:/ {ns=$2}
/volumes:/ {in_vol=1}
in_vol && /secret:/ {sv=1}
in_vol && sv && /name:/ && $1=="name:" {
printf "Pod %s/%s mounts secret %s as a volume (files)\n", ns, name, $2
}
'

How to use this with the earlier outputs:

  • Secrets used only as env vars (and not shown here as mounted volumes) are the strongest candidates for change.
  • For each such workload, human review is required to decide if the application can be modified to read from files instead of environment variables; kubectl cannot make that decision.

5. Verification after any manual refactor

Once you have updated application code and pod specs (outside this step):

kubectl get deploy,sts,ds,job,cronjob -A -o yaml | \
grep -E "envFrom:|secretRef:|secretKeyRef:"

You should confirm, by inspecting the YAML, that:

  • Secrets are no longer referenced under envFrom.secretRef or env.valueFrom.secretKeyRef where you intended to migrate to files.
  • The same or equivalent Secrets are instead referenced under volumes[].secret and volumeMounts[], and your application reads those files.
Automation
#!/usr/bin/env bash
#
# Report pods that consume Kubernetes Secrets via environment variables
# (vs mounted secret files) across the entire cluster.
#
# Run on: any machine with kubectl access and current kubeconfig context set.

set -euo pipefail

echo "Scanning all namespaces for pods using Secrets as environment variables..."
echo

# 1) List all pods and inspect env / envFrom for secretRefs
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
containers: [
.spec.containers[]? as $c
| {
name: $c.name,
envSecrets: [
$c.env[]?
| select(.valueFrom.secretKeyRef != null)
| {
envName: .name,
secretName: .valueFrom.secretKeyRef.name,
secretKey: .valueFrom.secretKeyRef.key
}
],
envFromSecrets: [
$c.envFrom[]?
| select(.secretRef != null)
| {
secretName: .secretRef.name,
optional: ( .secretRef.optional // false )
}
]
}
]
}
| select([ .containers[].envSecrets[], .containers[].envFromSecrets[] ] | length > 0)
| @json
' | jq -r '
. as $pod |
"NAMESPACE: \($pod.ns)\nPOD: \($pod.pod)\n" +
(
$pod.containers[]
| select((.envSecrets | length > 0) or (.envFromSecrets | length > 0))
| " CONTAINER: \(.name)\n"
+ (
if (.envSecrets | length) > 0 then
" env secrets (valueFrom.secretKeyRef):\n"
+ (
.envSecrets[]
| " - env: \(.envName)\n secret: \(.secretName)\n key: \(.secretKey)\n"
)
else "" end
)
+ (
if (.envFromSecrets | length) > 0 then
" envFrom secrets (secretRef):\n"
+ (
.envFromSecrets[]
| " - secret: \(.secretName)\n optional: \(.optional)\n"
)
else "" end
)
)
+ "\n-----------------------------\n"
'

echo "Scan complete."
echo
echo "Interpretation:"
echo "- Any listed pod/container is using Kubernetes Secrets as environment variables."
echo "- For CIS OKE 4.4.1, these are the workloads to review and, where feasible,"
echo " refactor to consume secrets from mounted secret files instead of env vars."

What output indicates a problem

  • Any NAMESPACE / POD / CONTAINER block printed by this script is a candidate finding.
  • Lines under:
    • env secrets (valueFrom.secretKeyRef): or
    • envFrom secrets (secretRef): show specific environment variables or envFrom blocks that rely on Secrets.
  • For each such workload, review the application and manifests to see if you can replace those environment-variable–based secrets with volume-mounted secret files.

Additional Reading: