Skip to main content
cd ../config-traps
risk/register/base64-configmaps-meet-etcds-identity-provider-default.html
Kubernetes Secrets Managementcritical severityKubernetes

Base64 ConfigMaps Meet etcd's Identity Provider Default

Severity
critical
Reviewed
24 Jul 2026
Remediation
~20 minutes
Overview

A ConfigMap holding a base64 database password looks encoded but is stored as flat, retrievable cleartext in etcd, in kubelet caches, and in every replica that mounts it. When etcd encryption at rest is left on its default identity provider, that same string also sits unencrypted in snapshots, Velero backups, and any copy of the etcd data directory, entirely outside the RBAC boundary that Secret objects are meant to enforce.

Operational summary

At a glance

Symptom
Any principal with configmaps get/list RBAC access, or access to an etcd snapshot or Velero backup, retrieves the credential in cleartext.
Likely cause
Credentials stored as base64 strings inside ConfigMap objects while etcd encryption at rest remains on the default identity provider.
Impact
Cluster-wide credential exposure across every namespace, backup, and replica, with no reliable way to enumerate which ConfigMaps hold live secrets during rotation.
Verification signal
Re-run the detection test and a controlled negative-path test.
Safe correction
Migrate credential data into Secret objects, block sensitive key patterns in ConfigMaps via Kyverno, and enable aescbc EncryptionConfiguration ahead of the identity provider.
Rollback or recovery
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

The Trap

Base64 ConfigMap Credentials Without etcd Encryption at Rest. Developers create a ConfigMap and populate its data field with a base64-encoded database password, API key, or TLS bootstrap token because the encoded string looks obfuscated. They pair this with a cluster running kube-apiserver’s default EncryptionConfiguration, which lists the identity provider first (or omits the file entirely), meaning etcd stores every object, ConfigMap or Secret, as unencrypted protobuf on disk.

The Default State

kubectl imposes no schema restriction on ConfigMap keys, so kubectl create configmap db-config --from-literal=password=cGFzc3dvcmQ= succeeds without warning. Base64 is an encoding, not a cipher; anyone with configmaps get/list reverses it with a single base64 -d call. Meanwhile the API server’s encryption-at-rest configuration, if set at all, typically ships with providers: [{identity: {}}] ahead of aescbc, so etcd itself never encrypts the value regardless of which object type stores it.

The Blast Radius

RBAC ClusterRoles built for read-only dashboards, CI pipelines, and Helm operators routinely grant broad get/list on configmaps because the resource is assumed low-sensitivity, while equivalent access to secrets is scoped tightly. That assumption fails silently once credentials live in ConfigMaps. Worse, etcd snapshots taken for disaster recovery, Velero backup archives, and any stolen or misconfigured PersistentVolume backing the etcd data directory now expose every credential in cleartext, with no encryption boundary to slow an attacker down. Rotation becomes guesswork because there is no consistent object type marking which ConfigMaps carry live secrets, so incident response cannot enumerate exposure with a single kubectl get secrets --all-namespaces query.

The Lead Mechanic Fix

Migrate every credential-bearing ConfigMap to a proper Secret object, then re-key etcd: set --encryption-provider-config=/etc/kubernetes/encryption-config.yaml on kube-apiserver with providers: [aescbc, identity] and a KMS-backed key, then force re-encryption with kubectl get secrets --all-namespaces -o json | kubectl replace -f -. Enforce a Kyverno validate policy that rejects ConfigMap keys matching patterns such as password, token, or apikey, redirecting authors to Secret objects at admission time.

03

Apply the safer control

Before you change production

Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.

Fix commands and configuration

--encryption-provider-config=/etc/kubernetes/encryption-config.yaml
providers: [aescbc, identity]
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
04

Verify, roll back or escalate

Verify

Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.

Rollback

Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

Escalate

Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.

After remediation

Further reading stays below the corrective workflow and is selected by platform, category and shared technical keywords.

Discover more

Connected KBY resources