Secrets Management: Protecting the Keys to your Kingdom¶
Estimated time to read: 4 minutes
In a world of automated infrastructure and CI/CD pipelines, "secrets" API keys, database passwords, TLS certificates, and SSH keys are the currency of access. If they are leaked, your entire security perimeter is compromised.
This guide explores how to manage secrets securely across the software lifecycle, moving from basic environment variables to advanced dynamic credentials.
1. What counts as a Secret?¶
A secret is any piece of data that grants access to a protected resource. * Authentication: API keys (AWS, Stripe, OpenAI), passwords, and tokens. * Infrastructure: Database connection strings, encryption keys. * Identity: Private SSH keys, SSL/TLS certificates.
2. The Hierarchy of Secrets Security¶
The OpsAtScale Maturity Framework tracks how organisations manage these sensitive values.
🔴 Level 0: Hardcoded (Static)¶
Risk: Extreme. Secrets are written directly into source code or committed in .env or config.json files.
Never do this. Once a secret is committed to Git, it is compromised forever, even if you delete it in the next commit.
🟡 Level 1: Environment Variables¶
Risk: Medium. Secrets are stored in the CI/CD tool (e.g., GitHub Secrets) and injected as environment variables during build or runtime. * Benefit: Secrets stay out of Git. * Risk: They are often "logged" by mistake in CI logs or visible to processes on the same machine.
🔵 Level 2: Managed Secret Stores¶
Risk: Low. Secrets are stored in a dedicated, encrypted service like AWS Secrets Manager, Azure Key Vault, or GCP Secret Manager. * Benefit: Centralised control, audit logging, and automated rotation. * Pattern: The application authenticates to the vault at runtime and pulls only what it needs.
🟢 Level 3: Dynamic Credentials & OIDC¶
Risk: Critical Minimum. This is the modern gold standard. Instead of storing a "long-lived" secret, the system generates a "dynamic" credential that expires in minutes. * OIDC Federation: Your GitHub Action authenticates to AWS via a "trust relationship" no password or key is stored in GitHub. See the example in CI/CD Pipeline Design. * Dynamic Secrets: HashiCorp Vault can generate a unique database password for a single CI run and delete it immediately after the run ends.
3. Best Practices for Developers¶
Use .gitignore religiously¶
Ensure that common secret files are always ignored globally.
Shift-Left Secret Detection¶
Use tools to scan your code before you push to the remote. * GitLeaks / TruffleHog: Scans your history for patterns like AWS keys or Stripe tokens. * Detection in CI: Integrate a secret scanner into your CI/CD Pipeline as a mandatory gate.
Rotate Secrets Regularly¶
The longer a secret exists, the higher the probability it has been compromised. Automate rotation where possible.
4. Tooling Comparison¶
| Tool | Model | Best For |
|---|---|---|
| HashiCorp Vault | Centralized | Multicloud, high-compliance, dynamic secrets |
| AWS Secrets Manager | Cloud-native | Teams exclusively on AWS; easy rotation |
| GCP / Azure Vault | Cloud-native | Native platform integration |
| Mozilla SOPS | GitOps-friendly | Encrypting secrets within Git (using PGK/KMS) |
| Infisical / Doppler | Developer-first | Simplified workflows for small to mid teams |
5. Summary Checklist¶
- Audit: Do a global search for "key", "password", "secret" in your current repos.
- Revoke: If a secret was EVER in Git, revoke it immediately and issue a new one.
- Migrate: Move from local
.envfiles to a managed secret store. - Federate: Switch CI/CD pipelines to OIDC federation (Zero-Secret-Store).
- Scan: Enable pre-commit hooks for all developers to prevent local leaks.