skip to content

A teammate says Kubernetes Secrets are safe because "the values are base64-encrypted". What is actually stored, and which protections do and do not apply?

level: juniorimportance: must knowfreq 72%

answer

  1. base64 = encoding, `base64 -d` reverses it
  2. plaintext in etcd unless EncryptionConfiguration
  3. list secrets ≈ read all of them
  4. Secret volumes are tmpfs (memory)
  5. Pod-create in a namespace ≈ Secret-read

basics

~20 s

Base64 is encoding, not encryption — anyone can decode it. A Secret's value is stored as-is in etcd, plaintext by default, and readable by anyone with API read access to it. Real protection comes from RBAC, encryption at rest, and limiting who and what can read it.

solid answer

~50 s

Base64 is a transport encoding so arbitrary bytes survive JSON/YAML; `base64 -d` reverses it with no key. Nothing about a Secret is encrypted by default. What actually protects a Secret: - **RBAC** — a Secret is a normal API object, so anyone with `get`/`list` on it in that namespace reads the value. `list` on Secrets cluster-wide is effectively cluster-admin. - **Encryption at rest** — only if the API server was started with an `EncryptionConfiguration`; otherwise the value sits in plaintext in etcd, so etcd backups and disk snapshots contain it. - **Node scoping** — the Node authorizer plus NodeRestriction admission limit a kubelet to Secrets used by Pods on its own node. - **On-node handling** — Secret volumes are backed by memory (tmpfs), so they are not written to node disk. What it does not protect against: anyone who can read the object, exec into the Pod, create a Pod that mounts it, or read an unencrypted etcd backup.

code

bash · 9 lines
bash
kubectl create secret generic db --from-literal=password='s3cr3t'
kubectl get secret db -o jsonpath='{.data.password}' | base64 -d
# s3cr3t

# describe deliberately hides values
kubectl describe secret db
# Data
# ====
# password:  6 bytes

go deeper

for a junior

Be able to state plainly that base64 is reversible encoding, show the decode command, and name RBAC as the thing that actually controls access.

for a middle

Add the storage picture: plaintext in etcd unless an EncryptionConfiguration is set, tmpfs on the node, and why list on Secrets is so broad.

for a senior

Discuss the threat model end to end — etcd backups and snapshots, node compromise with the Node authorizer and NodeRestriction, Pod-create as an equivalent of Secret-read, and exec access.

for a principal

Position native Secrets as a distribution mechanism with no versioning or rotation, and say what you would layer on top and why, with the compliance and blast-radius arguments.

## What base64 is Base64 maps arbitrary bytes onto 64 printable ASCII characters. It exists because a Secret must be able to hold binary data (a DER-encoded key, a keytab, a certificate) while the API represents objects as JSON. It has no key, no secret parameter and no reversibility barrier — `echo cGFzc3dvcmQ= | base64 -d` prints `password`. Treating it as protection is the single most common Kubernetes security misconception, and interviewers ask about it precisely because it separates candidates who have read the docs from candidates who have read the field names. For convenience the API also accepts `stringData` on write: you supply plain UTF-8, the API server encodes it, and reads always come back as base64 in `data`. That is purely ergonomic. ## What a Secret actually is A Secret is a namespaced API object holding up to ~1 MiB of key/value data, with a `type` field. Compared with a ConfigMap the differences are: - values are base64 in the serialised form; - the API server can be configured to encrypt Secrets (only Secrets, by default) before writing to etcd; - the kubelet mounts Secret volumes on a `tmpfs` (memory-backed) filesystem, so contents are not persisted to node disk and vanish when the Pod goes; - some tools redact them (`kubectl describe secret` shows sizes, not values); - RBAC rules and audit policies commonly treat them differently. Notably, everything else is the same as a ConfigMap. A Secret is *handled* more carefully, not cryptographically transformed. ## The four real protections **1. RBAC.** This is the primary control. `get` on a specific Secret leaks that Secret; `list` or `watch` on Secrets in a namespace leaks all of them, because list responses contain the full objects. Cluster-wide `list secrets` is functionally cluster-admin: with the right token you can read every credential in the cluster. So the review question for any Role is not "is it read-only?" but "does it include Secrets?". **2. Encryption at rest.** Absent an `EncryptionConfiguration` on the API server, the value is written to etcd verbatim. That means an etcd backup file, a volume snapshot of the control plane, or filesystem access on a control-plane node hands over every credential. Managed providers often enable envelope encryption backed by a cloud KMS by default; self-managed clusters frequently do not. "Encrypted at rest" is an operator decision, never an automatic property of the Secret kind. **3. Node scoping.** The Node authorization mode restricts each kubelet to reading only Secrets referenced by Pods scheduled to its node, and the NodeRestriction admission plugin stops a kubelet from editing objects to widen that. Without both, a compromised node reads the whole cluster's Secrets. **4. On-node handling.** Secret volumes are `tmpfs`; the bytes live in node memory and are not written to disk (they can still reach swap on nodes with swap enabled, and a node memory dump contains them). ## What none of this stops - Anyone who can `kubectl exec` into a Pod that mounts the Secret can simply read the file. - Anyone who can create Pods in a namespace can mount any Secret in that namespace and print it — Pod-create is close to Secret-read. - Anyone who can read an unencrypted etcd snapshot. - Application code that logs the value, or a crash dump that includes it. ## How to answer the teammate Say three things. First, base64 is encoding — demonstrate the one-line decode. Second, whether Secrets are encrypted at rest depends on the API server's `EncryptionConfiguration`, so go check yours rather than assume. Third, the effective access control is RBAC plus who can create Pods in the namespace; if a service account can list Secrets or create arbitrary Pods, that is where the exposure is, not the encoding. ## The honest framing Kubernetes Secrets are a *distribution* mechanism with a modest hardening story, not a secret-management product. They give you namespaced storage, RBAC-gated access, optional at-rest encryption and memory-backed delivery to Pods. They do not give you versioning, automatic rotation, leases, per-request auditing of the value, or protection from a namespace-admin. Teams that need those bolt an external system on top — but the base64 point stands regardless of what you choose above it.

  • If base64 gives nothing, what is actually different between a Secret and a ConfigMap?
    Handling, not cryptography. Secrets can be encrypted at rest by the API server, are mounted from memory-backed tmpfs rather than node disk, are redacted by tools like `kubectl describe`, and are the object kind that RBAC and audit policy usually single out. The storage model, size limit and consumption mechanics are otherwise the same as a ConfigMap.
  • Why is `list` permission on Secrets so much more dangerous than it sounds?
    A LIST response contains the full objects, including their data, so `list secrets` in a namespace exposes every credential in it — there is no metadata-only list. Granting it cluster-wide is effectively cluster-admin. The same applies to `watch`. Roles should name specific Secrets with `get` and `resourceNames` when a workload genuinely needs API access to one.

Base64 is writing a password in a different alphabet, not locking it in a safe — anyone who knows the alphabet reads it straight off.

saying these in an interview costs you the question

  • Saying base64 is "a light form of encryption" or that decoding needs a key.
  • Assuming every cluster encrypts Secrets in etcd by default.
  • Believing `kubectl describe secret` hiding values means the values are protected.
  • Overlooking that anyone able to create a Pod in the namespace can mount and read its Secrets.
  • Thinking a Secret is safe because the YAML in git is base64-encoded.

context