Post

Kubernetes Secret vs. ConfigMap: Why Use Secrets If Base64 Isn't Encryption?

Why store credentials in Kubernetes Secrets when Base64 isn't encryption? A practical look at RBAC, encryption at rest, and how Secrets work with credential management tools.

Kubernetes Secret vs. ConfigMap: Why Use Secrets If Base64 Isn't Encryption?

I recently came across a Kubernetes meme about someone storing passwords in a ConfigMap.

A Kubernetes meme about storing passwords in a ConfigMap

If you’ve spent time running Kubernetes, you probably get the joke. If you’re new to it, though, the problem might not be so obvious.

A ConfigMap stores the password as plain text. A Secret’s data field looks different, but it’s just the same value encoded in Base64.

That doesn’t actually encrypt anything. So why are we supposed to put passwords in Secrets instead of ConfigMaps?

I thought that was a question worth spending a little time on.

If Secrets Are Just Base64, Why Not Use a ConfigMap?

Secrets let us manage access to credentials separately from ordinary configuration through RBAC. They also work with encryption at rest and the credential management tools in the Kubernetes ecosystem. Those protections need to be configured, though. Creating a Secret doesn’t automatically encrypt the password. Kubernetes Secrets documentation

Looking only at the Base64 in the YAML doesn’t tell us much about why Secrets are useful. We also need to look at who can read the password, whether it’s encrypted when stored in etcd, and which tools can manage it.

How Secrets and ConfigMaps Compare

A ConfigMap holds ordinary configuration, while a Secret holds sensitive data such as passwords and tokens. Some of their capabilities overlap: both support RBAC and can be configured for encryption at rest. ConfigMap documentation

ComparisonConfigMapSecret
Intended useOrdinary configurationPasswords, tokens, keys, certificates
API representationPlain-text strings in data; Base64 in binaryDataBase64 in data; accepts plain-text input through stringData
Default etcd encryptionAPI-level encryption at rest is not enabled by default in Kubernetes; check the cluster configurationAPI-level encryption at rest is not enabled by default in Kubernetes; check the cluster configuration
Encryption at rest✅ Can be configured✅ Can be configured; recommended in the official guidance
Sensitive-data handling by Kubernetes and toolsTreated as ordinary configuration; don’t expect tools to hide the contentsIntended for sensitive data; tools may take extra precautions, but masking isn’t guaranteed
Separate RBAC permissions✅ Can have its own permissions✅ Can be authorized separately from ConfigMaps; stricter permissions must be configured
Secret store / Vault / KMS integrationNot the usual resource for credential integrations; KMS-backed encryption at rest is still possible✅ Common resource for external credential integrations; also supports KMS-backed encryption at rest
Output from tools such as kubectl describekubectl describe configmap displays the datakubectl describe secret usually shows keys and sizes; kubectl get secret -o yaml still returns the Base64 values

You might still be thinking, “If a ConfigMap can have access controls and encryption too, why separate them?” Database configuration makes a useful example.

First, Secrets Can Have Their Own RBAC Permissions

Suppose someone is troubleshooting a database connection. They may need to see DB_HOST, but they don’t necessarily need DB_PASSWORD.

If both live in a ConfigMap, permission to read the configuration also exposes the password. Put DB_PASSWORD in a separate Secret, and you can grant access to just the ConfigMap. For a ServiceAccount, the relevant part of a Role could look like this:

1
2
3
4
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list"]

There’s no secrets entry here, so this rule doesn’t grant access to Secrets. A user, controller, or workload that only needs to inspect configuration doesn’t have to get the password as well.

One detail is easy to miss: RBAC permissions are additive. If another RoleBinding or ClusterRoleBinding already grants access to Secrets, this rule won’t take it away.

Pod creation permissions matter too. Someone who can create a Pod may be able to read a Secret by mounting it. And list and watch can expose Secret contents as well, so restricting only get isn’t enough. Secret access control guidance

Second, Secrets Can Use Kubernetes Encryption at Rest

Once access is sorted out, there’s the question of how the data is stored.

The data field may just be Base64, but you can configure an encryption provider on the Kubernetes API server to encrypt the data before writing it to etcd:

1
2
3
4
5
Kubernetes API Server
    ↓ Encryption Provider
Encrypted Secret data
    ↓
etcd

Encryption at rest is actual encryption: you need the key to recover the data. It’s separate from the Base64 you see in YAML.

Kubernetes doesn’t enable API-level encryption at rest for Secrets by default. If you’re using a managed cluster, check the service’s configuration. ConfigMaps can also be included, so kind: Secret alone doesn’t tell you whether the stored data is encrypted. Encryption at rest documentation

Whether encryption stops someone from reading the password depends on where they’re reading it from.

If etcd files or snapshots leak, encryption provides protection as long as the keys haven’t leaked too. But if someone already has permission to run kubectl get secret -o yaml, the API server will still return values they can decode. That’s why we need both encryption and RBAC.

When enabling encryption, remember that existing data needs to be rewritten to apply it. Old backups won’t become encrypted automatically either. Encrypting existing data

Third, Many Kubernetes Security Tools Integrate with Secrets

We don’t necessarily have to maintain every Secret YAML file by hand.

External Secrets Operator, HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, Secrets Store CSI Driver, and Sealed Secrets all come up in this area, although they do different jobs.

For example, you can keep passwords in Vault or a cloud secret manager. External Secrets Operator can retrieve them and create Kubernetes Secrets for Pods to use. External Secrets Operator documentation

1
2
3
4
5
6
7
Vault / AWS Secrets Manager / GCP Secret Manager / Azure Key Vault
    ↓
External Secrets Operator
    ↓
Kubernetes Secret
    ↓
Pod

That lets Git hold the configuration describing where to retrieve the credentials, without containing the passwords themselves. Once synced into the cluster, the resulting Secret still needs the access controls and encryption discussed above.

Another option is Secrets Store CSI Driver, which mounts external credentials directly as files inside a Pod:

1
2
3
4
5
Vault / Cloud Secret Manager
    ↓
Secrets Store CSI Driver + provider
    ↓
Pod filesystem

This doesn’t necessarily create a Kubernetes Secret. If the application needs one, you can enable syncing separately. A Pod must mount the corresponding CSI volume to trigger that sync. CSI Secret sync documentation

Sealed Secrets takes a different approach: it encrypts the data into a SealedSecret that can be stored in Git. A controller in the cluster decrypts it and creates a regular Kubernetes Secret. Sealed Secrets documentation

These tools don’t all depend on Kubernetes Secrets for storage, but Secrets are a common resource they use when integrating with Kubernetes. Using Secrets for credentials makes it easier to work with those tools.

KMS often comes up in the same discussion. In Kubernetes encryption-at-rest integrations, its job is to manage encryption keys. That’s different from a secret manager that stores passwords and tokens.

A Secret Doesn’t Make a Password Safe by Itself

With that in mind, take another look at this YAML. The password below is just an example:

1
2
3
4
5
6
7
apiVersion: v1
kind: Secret
metadata:
  name: demo-credentials
type: Opaque
data:
  password: cGFzc3dvcmQ=

In a shell with the base64 command available, recovering it is straightforward:

1
2
printf '%s' 'cGFzc3dvcmQ=' | base64 -d
# Output: password

If you commit a Secret YAML file containing real credentials to Git, anyone who can read the file can still get the password. Switching to stringData doesn’t help; that field is just a convenient way to supply plain-text input. Secret documentation

In production, you still need to restrict Secret access, configure encryption at rest, rotate credentials, and audit access. For GitOps, you can store references to external credentials or appropriately encrypted resources, as in the examples above. Secret security practices

Final Thoughts

Looking back at the meme, I think the confusing part is treating “using a Secret” and “the password is encrypted” as the same thing. Secrets let us manage passwords separately from ordinary configuration and work with encryption and external credential tools. But we still have to set those things up.

So yes, passwords belong in Secrets. We just can’t stop there and assume they’re safe. Base64-encode a password and commit it to Git, and it’s still an exposed password.

For an example of mounting and syncing external credentials, see my post on fixing Secrets Store CSI sync on EKS.

References

This post is licensed under CC BY 4.0 by the author.

Trending Tags