KubernetesLXV · Secrets SecuritySecrets security
Secret mounting risks — how Secrets leak
What you'll learn
- Identify the risks of mounting Secrets as env vars vs volume mounts
- Set the right file permissions on mounted Secrets
- Recognise the leak vectors (shell history, crash dumps, /proc)
- Apply the production patterns for Secret consumption
Prerequisites
Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16
Mounting a Secret into a Pod is the operational moment of risk: the Secret value is delivered to a container that may have arbitrary code. The mounting choice (env var vs volume mount) determines the exposure surface; the file permissions determine who in the container can read it. This lesson covers the mounting options, the leak vectors, and the production patterns.
The two mounting options
A Secret can be delivered to a container in two ways:
- Environment variable. The Secret value is injected as an env var:
spec:
containers:
- name: api
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
- Volume mount. The Secret is mounted as a file:
spec:
containers:
- name: api
volumeMounts:
- name: db-credentials
mountPath: /etc/secrets/db
readOnly: true
volumes:
- name: db-credentials
secret:
secretName: db-credentials
defaultMode: 0400
flowchart LR
A[Secret] --> B{How mounted?}
B -->|env var| C[Every process sees it]
B -->|volume mount| D[File in mount path]
C --> E[Visible in /proc/<pid>/environ]
D --> F[File permissions limit access]
The leak vectors
Five vectors:
-
/proc/<pid>/environ. Any process in the container can read the env vars of any other process. A malicious sidecar or a compromised workload can dump every Secret. -
Shell history. A shell that sources an env var (e.g.,
export $(cat /etc/secrets/db/password)) may save the value in~/.bash_history. The fix is to read the value into a variable without echoing it. -
Crash dumps. A workload that crashes may include the env vars in the crash report. The fix is to scrub the env vars from the crash report.
-
Logs. A workload that logs its configuration may include the Secret value. The fix is to scrub the Secret value from logs.
-
Container escape. A privileged container that escapes its mount namespace can read the host’s
/proc/<pid>/environ, which includes the workload’s env vars. The fix is PSSrestricted.
Volume mounts are safer
A volume mount with defaultMode: 0400 is safer than
an env var:
volumes:
- name: db-credentials
secret:
secretName: db-credentials
defaultMode: 0400 # readable only by the file's owner
The kubelet creates the file with mode 0400. Only
the process that runs as the file’s UID can read it.
Other processes in the container cannot read it.
The kubelet audit
The kubelet’s --audit flag records every Secret
mount:
# Verify the kubelet flags
cat /var/lib/kubelet/config.yaml | grep -E "read-only-port|anonymous-auth"
The audit log records the Pod’s UID, the Secret name, and the mount path. The SIEM alerts on unexpected Secret mounts.
Production patterns
- Volume mounts with
0400for production Secrets. Env vars are acceptable for non-sensitive configuration. runAsUsernon-zero. The file owner is a non-root user; the Secret is not readable by root.readOnly: trueon the mount. The workload cannot write to the mount path; the Secret is immutable from the workload’s perspective.- No
kubectl execwith the Secret. The workload’s audit log records everykubectl execthat accesses the Secret; the operator verifies.
Common failure modes
- Env vars used for high-value Secrets. The
Secret is visible in
/proc. The fix is a volume mount with0400. defaultMode: 0644on the mount. The file is world-readable; any process in the container can read it. The fix is0400.runAsUser: 0. The file is owned by root; any process in the container can read it. The fix isrunAsUser: <non-zero>.- Workload logs the Secret. The Secret value appears in the workload’s logs. The fix is a redaction list in the logger plus a drop rule at log ingest; the value otherwise survives in the log store long after the Secret is rotated.
Production failure modes
- Secret mounted but not needed. A workload that does not use the Secret has the Secret in its environment. The fix is to remove the mount.
- Multiple Secrets in one Pod. A Pod with many
Secrets has many files in
/etc/secrets. The fix is to scope the Secrets per-container. - Secret rotation not handled. A workload that reads the Secret at startup does not pick up the rotated value. The fix is to re-read periodically.
- Crash dump leaks. A workload that crashes dumps the env vars in the crash report. The fix is to scrub the report.
Cross-course references
- The Linux course covers file permissions,
/proc, and shell history. - The Observability course covers the audit log entries for Secret access.
Quiz
Knowledge check · 4 questions
Q1. Where does an attacker in a compromised container find an env var Secret?
Q2. A volume mount with `defaultMode: 0400` is safe even if the Pod runs as root.
Q3. Your workload mounts a database credential as an env var (`DB_PASSWORD`). A compromised sidecar reads `/proc/1/environ` and exfiltrates the credential. Walk the response.
The Pod has an `api` container and a `metrics-exporter` sidecar. The `api` container has the `DB_PASSWORD` env var. The `metrics-exporter` sidecar was compromised via a known CVE. The sidecar reads `/proc/1/environ` and exfiltrates the credential.
Q4. Name three leak vectors for Secrets mounted in containers.
Passing score: 75%. Answers are checked in this browser.
Production discipline
The mounting choice determines the exposure surface.
A defensible Secret programme uses volume mounts with
0400 permissions and runAsUser: <non-zero> for
high-value Secrets, env vars for non-sensitive
configuration, and scrubs Secret values from logs,
crash dumps, and shell history. The audit log
records every Secret access; the SIEM alerts on
unexpected access. A cluster whose Secrets are
mounted safely has a Secret programme that is
auditable.