Don’t Hardcode – Managing Secrets the Right Way
Duration: ~20 minutes
Overview
Keep passwords out of Git and YAML. Store them as Secrets, mount or inject them at runtime, and rotate without rewriting every deployment.
Replace a hardcoded credential with a Secret injected as an env var and as a file mount, then rotate it and compare how each injection updates.
Why it matters
Credentials in manifests or images land in Git history and travel with every clone. Secrets keep the values out of source, limit who can read them with RBAC, and make rotation a Secret update instead of a code change. File mounts refresh on update; env vars usually need a restart.
What does it solve
-
Git history leakage
-
Rotation friction
-
Broad manifest visibility
-
Credential reuse temptation
Your Mission
Credential hunters love passwords parked in Git and Deployment YAML—they survive forever in history. Catch the insecure pattern, strip it, then inject Secrets in ways attackers cannot scrape from manifests, and prove rotation still works.
Click each step only if you need a hint.
Open a secrets sandbox
oc new-project 101-06-s-secrets
Simulate the leak — hardcoded credential in the Deployment
Show how easy loot is when passwords live in env literals.
oc create deployment insecure-app --image=registry.access.redhat.com/ubi9/ubi -- sleep infinity
oc set env deployment/insecure-app DB_PASSWORD=SuperSecret123
oc get deploy insecure-app -o yaml | grep -n 'SuperSecret'
Tear down the leaky workload
oc delete deployment insecure-app
Move the credential into a Secret
oc create secret generic db-credentials --from-literal=DB_PASSWORD=SuperSecret123
Defend with Secret-as-env (no plaintext in the Deployment)
oc create deployment secure-app-env --image=registry.access.redhat.com/ubi9/ubi -- sleep infinity
oc set env deployment/secure-app-env --from=secret/db-credentials
oc exec deploy/secure-app-env -- printenv | grep DB_PASSWORD
oc get deploy secure-app-env -o yaml | grep -n 'SuperSecret'
Defend with Secret-as-file (rotation-friendly)
oc create deployment secure-app-file --image=registry.access.redhat.com/ubi9/ubi -- sleep infinity
oc set volume deployment/secure-app-file --add --name=creds --type=secret --secret-name=db-credentials --mount-path=/opt/creds --read-only
oc exec deploy/secure-app-file -- ls /opt/creds
oc exec deploy/secure-app-file -- cat /opt/creds/DB_PASSWORD
Rotate under pressure — prove file updates vs env restart
After compromise suspicion, rotation must stick. File mounts refresh; env needs a restart—know which path you chose.
oc create secret generic db-credentials --from-literal=DB_PASSWORD=NewValue456 -o yaml --dry-run=client | oc apply -f -
oc exec deploy/secure-app-file -- cat /opt/creds/DB_PASSWORD
oc rollout restart deployment/secure-app-env
oc exec deploy/secure-app-env -- printenv | grep DB_PASSWORD
Cleanup
oc delete project 101-06-s-secrets --wait=false
Debrief
You moved a hardcoded credential into a Kubernetes Secret, consumed it as env and as a file mount, and compared rotation behavior under pressure.
What breaks without this:
-
Credentials in Git, Dockerfiles, or Deployment YAML → trivial loot after a foothold
-
Env-only mounts → rotation often needs a restart
-
Secrets without RBAC / etcd encryption → broad read and disk exposure
Controls that matter: API Secrets (not plaintext in manifests), prefer file mounts for rotation, restrict get/list, enable etcd encryption, and plan external secret backends for mature environments.
Quick facts: values are base64-encoded, not encrypted, unless etcd encryption (or an external store) is on. Secrets alone do not stop exfiltration—pair with least-privilege RBAC.
