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:​