# Secrets Management ## SOPS + age SOPS with age encryption is the standard across all projects. A single `.sops.yaml` at the repo root defines path-based encryption rules. ### File Naming - `.sops.yaml` path-based rules match specific filename patterns (e.g., `**/*secret*.yaml`) - Non-secret files must NOT contain `secret` in their name, or the pre-commit hook will encrypt them - KSOPS generator files should be named `ksops-generator.yaml`, not `secret-generator.yaml` ### encrypted_regex Gotcha When using `encrypted_regex` for selective field encryption (e.g., Ansible group_vars), variable names must contain a keyword that matches the regex (e.g., `password|private_key|api_key|secret|token`). Arbitrary key names are silently left unencrypted. ### SOPS Vars Plugin Each Ansible project needs `vars_plugins_enabled = host_group_vars,community.sops.sops` in `ansible.cfg`. Files in `group_vars/` must be named after a group (e.g., `all.sops.yaml`), not arbitrary names. ### Interactive Editor Pitfalls - `sops ` opens an interactive editor — fails in non-interactive sessions - `sops -e /tmp/file` fails when the temp path doesn't match `.sops.yaml` rules - Multiple `sops --set` calls can corrupt files — use the interactive editor for multi-field edits ## Credential Handling - **Never pass secrets via command-line arguments** — visible in `ps` output - Use `@file` references, environment variables sourced at runtime, or stdin - For Ansible, use temp files with `trap rm` cleanup: `-e "@${tmpfile}"` - Read secrets at execution time and use them ephemerally — never cache or persist values - Reference the **existence** of a secret file in docs, never its contents ## Bootstrap Secrets Some secrets are chicken-and-egg (e.g., the age decryption key for ArgoCD's KSOPS). These must be created manually as a bootstrap step and documented clearly. ## Credential Lifecycle Management - **Track credential expiry dates.** OAuth client secrets, API tokens, and certificates have expiry dates that can cause silent failures. Document expiry dates when creating credentials. - **Set alerts before expiry.** For long-lived credentials (e.g., 720-day OAuth client secrets), set calendar reminders or automated monitoring alerts well before they expire. - **Rotation plan.** Know the rotation procedure before you need it — some credential types (e.g., Azure app registrations) require coordinated updates across multiple systems. ## Multi-Field Secret Files Secret files that contain multiple fields (e.g., repo URL, token, username) cannot be used as bare values. Consumers must parse individual fields (e.g., `grep + awk` or structured YAML/JSON parsing). The multi-field format is preferable because it's self-documenting — all related credentials live together. But any automation reading the file needs extraction logic, not just `cat`. ## Generating Secrets with gen-secret Use the `gen-secret` script (from `small-scripts`, symlinked to `~/sbin/gen-secret`) to generate cryptographically random strings that are safe for bash, YAML, and JSON without escaping. ### Workflow: Generate + SOPS Encrypt 1. **Generate the secret value** — use `gen-secret` with an appropriate length: ```bash SESSION_SECRET=$(gen-secret 48) # 48-char session key API_KEY=$(gen-secret 20) # 20-char access key API_SECRET=$(gen-secret 40) # 40-char secret key ``` 2. **Write plaintext YAML to the target path** — the file must be at the path matched by `.sops.yaml` rules (e.g., `**/*secret*.yaml`): ```bash cat > path/to/my-secret.sops.yaml <