CI Secret Injection
Secrets in CI. The patterns.
The secret injection problem
CI needs secrets and mishandled secrets leak in predictable ways: build logs, environment dumps, build artifacts. The discipline is injecting at runtime from a vault and never storing plaintext in the repo or CI config.
- CI needs secrets. Deploy keys, API tokens, database credentials per pipeline; real operational requirement, not optional.
- Mishandled secrets leak. Logs, env dumps, build artifacts per leak; each is recoverable post-leak only at high cost (rotation, customer notice, audit).
- Inject from vault at runtime. Runtime fetch per pipeline; never store in repo or CI config plaintext.
- No-plaintext-secret rule per org. Published policy per org catches casual secret pasting before it produces an incident.
Vault-backed secrets
Vault-backed secrets are the standard. OIDC for cloud-issued tokens removes long-lived credentials from CI; secrets managers cover stored credentials that cannot be issued on demand.
- OIDC-issued cloud tokens. GitHub Actions to AWS, GCP, Azure path per pipeline; no long-lived keys in CI.
- Secrets manager. AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault per cloud; pull at runtime rather than at config time.
- SaaS-friendly options. 1Password Connect, Doppler, Infisical per org; easier integration for stacks that are not cloud-heavy.
- Documented source per secret. Named vault and path per secret supports rotation and audit.
Scope and rotation
Scope and rotation limit blast radius. Minimum permissions per pipeline rather than a shared mega-token, quarterly rotation cadence, access audit so leak investigation is possible.
- Minimum per pipeline. Smallest secret set per pipeline; do not share a single CI token across all pipelines.
- Quarterly rotation. At-least-quarterly cadence per secret; vault-backed rotates automatically, static credentials need a manual schedule.
- Audit access. Who-and-what-pulled-when log per secret drives investigation if a secret leaks.
- Named owner per secret. Responsible team per secret catches abandoned credentials that nobody owns.
Avoid leaking in logs
Logs are the most common leak path. Mask known patterns, avoid set -x in shell scripts that handle secrets, run pre-commit scanners to catch secrets before they reach CI.
- Mask known patterns. Secret-masking config per pipeline; GitHub Actions masks
secrets.*references, verify the masking actually works. - No
set -xwith secrets. No-trace rule per script; trace mode in shell scripts dumps environment including secrets. - Pre-commit scanners. Gitleaks or TruffleHog hook per repo catches secrets before they reach CI.
- Post-incident log audit per pipeline. Searchable log review per leak catches near-misses before they become breaches.
How to migrate
Migration is staged. Inventory current secrets, roll out OIDC plus secrets manager pipeline-by-pipeline, track retirement of static secrets against a deadline.
- Inventory current secrets. Spreadsheet per org of where each secret is used; foundation for migration that big-bang attempts skip.
- One pipeline at a time. Staged OIDC plus secrets manager rollout per migration; big-bang migration breaks pipelines.
- Deadline to retire static secrets. Named retirement date per org with weekly progress tracking; without a deadline, static secrets never retire.
- Named owner per migration. Responsible engineer per migration catches stalled progress before it normalises.