Secrets
Kubernetes Secrets hold sensitive data — tokens, passwords, certs. Base64-encoded (NOT encrypted) by default; encrypt at rest in etcd, mount as env vars or files, never log them.
Create, mount, sealed / external secrets
EXAMPLE
# 1) Create a Secret (literals)
kubectl create secret generic db-credentials \
--from-literal=DB_USER=app \
--from-literal=DB_PASSWORD='s3cr3t-p@ss'
# From files
kubectl create secret generic tls-key --from-file=tls.crt --from-file=tls.key
# 2) YAML form — IMPORTANT: base64 encode values
echo -n 's3cr3t-p@ss' | base64
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
DB_USER: YXBw
DB_PASSWORD: czNjcjN0LXBAc3M=
# Or use 'stringData' to skip base64 (Kubernetes encodes for you):
stringData:
DB_USER: app
DB_PASSWORD: 's3cr3t-p@ss'
# 3) Consume as ENV vars
apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
selector: { matchLabels: { app: web } }
template:
metadata: { labels: { app: web } }
spec:
containers:
- name: web
image: myapp:1.0
envFrom:
- secretRef: { name: db-credentials } # all keys → env vars
env:
- name: STRIPE_KEY
valueFrom:
secretKeyRef:
name: stripe
key: api_key
# 4) Consume as a FILE (volume mount)
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: tls-files
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls-files
secret:
secretName: tls-key
items:
- key: tls.crt
path: tls.crt
mode: 0400
- key: tls.key
path: tls.key
mode: 0400
defaultMode: 0400
# 5) Built-in Secret TYPES — Kubernetes-aware schemas
# Opaque — generic (default)
# kubernetes.io/tls — { tls.crt, tls.key } for Ingress TLS
# kubernetes.io/dockerconfigjson — pull secret for private registries
# kubernetes.io/basic-auth — { username, password }
# kubernetes.io/ssh-auth — { ssh-privatekey }
# Image pull secret
kubectl create secret docker-registry ghcr-pull \
--docker-server=ghcr.io \
--docker-username=USER \
--docker-password=TOKEN \
--docker-email=ops@example.com
# Attach to a Pod
spec:
imagePullSecrets:
- name: ghcr-pull
# 6) Secret rotation
# Pods mounted as VOLUMES see updates within ~60s (kubelet polls)
# Pods consuming as ENV VARS do NOT see updates — restart needed
kubectl rollout restart deployment/web
# Versioned name pattern (no in-place rotation):
# db-credentials-v3 → bump the version, update Deployment spec → rolling restart
# 7) Immutable secrets (perf + safety)
apiVersion: v1
kind: Secret
metadata: { name: db-credentials-v3 }
immutable: true
data: { ... }
# Reduces kube-apiserver/kubelet load; prevents accidental edits.
# === Encryption + Access ===
# 8) Encrypt secrets at rest in etcd
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources: [secrets]
providers:
- aescbc:
keys:
- name: key1
secret: <base64-32-byte-key>
- identity: {}
# Reference in kube-apiserver: --encryption-provider-config=/etc/kubernetes/encryption-config.yaml
# Most managed services (EKS, GKE, AKS) offer this with KMS-backed keys.
# 9) RBAC — restrict who can read secrets
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: { name: secret-reader, namespace: app }
rules:
- apiGroups: ['']
resources: [secrets]
resourceNames: [db-credentials] # restrict to specific secret
verbs: [get]
# === Better: external secret managers ===
# 10) SealedSecrets (Bitnami) — encrypt secrets in git
# Install the SealedSecrets controller in the cluster
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
# sealed-secret.yaml is safe to commit; controller decrypts in-cluster.
# 11) External Secrets Operator — sync from AWS Secrets Manager, GCP Secret Manager, Vault, Doppler
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata: { name: aws-secretsmanager }
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef: { name: external-secrets }
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata: { name: db-credentials }
spec:
refreshInterval: '1h'
secretStoreRef: { name: aws-secretsmanager, kind: SecretStore }
target:
name: db-credentials # creates a k8s Secret
data:
- secretKey: DB_USER
remoteRef: { key: prod/app/db, property: user }
- secretKey: DB_PASSWORD
remoteRef: { key: prod/app/db, property: password }
# Rotation in the cloud → automatic propagation into the cluster.
# 12) HashiCorp Vault — agent injector pattern
# Pods get secrets injected as files by the Vault agent sidecar.
# Best for high-security environments + dynamic database creds.
# === Best practices ===
# 1. NEVER commit raw Secret manifests to git — use SealedSecrets or external-secrets
# 2. Mount as FILES with 0400 mode (instead of env vars) — fewer ways to leak
# 3. Per-Service Account credentials, not per-cluster
# 4. Restrict via RBAC — Pod can only read secrets it needs
# 5. Enable encryption-at-rest in etcd (KMS-backed in managed clusters)
# 6. Rotate regularly + automate (External Secrets + cloud rotation policies)
# 7. Audit access — kube-apiserver audit logs to SIEM
# 8. Don't print secrets to logs — use stdout-safe values only
# 9. Use immutable secrets for stable references
# 10. PolicyAgent (Kyverno, OPA Gatekeeper) — block plain Secret manifests in CI
Why it matters
Stop committing raw Secret manifests. Use SealedSecrets (encrypted in git) or External Secrets (synced from a cloud manager) so rotation in the source of truth flows into the cluster automatically.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
kubectl create secret generic db-pass --from-literal=password=s3cret # Mount the same way as ConfigMaps.Try it Yourself »
Discussion
Loading…