SOPS
SOPS (Secrets OPerationS) is an editor and encryption tool for structured files. It encrypts the values in a YAML file while leaving the keys readable, using a KMS key you name, so a secrets file can be reviewed, diffed and committed like any other code. The Secrets Blueprint is built around it.
What it does
sops file.yaml decrypts the file, opens your editor, and re-encrypts on save. Each value is encrypted separately; the file carries a metadata block naming the key that protects it. Anyone with kms:Decrypt on that key can read the file; anyone without cannot, whatever their Git access. A .sops.yaml at the repository root maps file paths to keys.
How BuiltForProd uses it
The acme-aws-blueprint-secrets repository has one folder per stage (sandbox, dev, staging, prod) and one YAML file per application per stage; sample.yaml and sample2.yaml show the layout. Its .sops.yaml routes each folder to a key in the security account: alias/acme-sops-dev for dev/, and so on.
Those keys are created by the AWS Baseline's sops-kms unit, one per stage with annual rotation. The key policies are the access control:
| Who | sandbox and dev keys | staging and prod keys |
|---|---|---|
| Platform Leads and DevOps Leads permission sets | encrypt and decrypt | encrypt and decrypt |
| Every other engineer permission set | encrypt and decrypt | no access |
acme-secrets-syncer (CI) | decrypt only | decrypt only |
The GitHub side mirrors this with CODEOWNERS: the developers team owns sandbox/ and dev/; the platform leads and infra admins own staging/, prod/, .github/, the scripts and .sops.yaml.
Two workflows move secrets to AWS. On a pull request, plan.yml assumes the syncer role through OIDC, runs scripts/validate.sh and scripts/push-to-ssm.sh --dry-run, and posts the list of parameters that would change. Validation fails on a file it cannot decrypt as well as on any decrypted value that is still a placeholder, and the push script fails before it writes anything if a required environment variable is missing, naming all of them at once.
On a push to main, sync.yml runs one job per stage (sandbox, dev, staging and prod); each job runs in the GitHub Environment named after its stage, assumes that stage account's deployer role, decrypts that stage's files and writes every key as a SecureString parameter at /acme/usw2/dev/<app>/<KEY>. A manual run narrows the matrix to the single stage the operator chooses, so no other account is touched; merging to main remains the normal path. From there the External Secrets Operator can deliver the values to Kubernetes.
Infrastructure-generated secrets, such as the DocumentDB master password, do not go through SOPS. The module that creates the resource writes them to Parameter Store directly.
Terms you will see
| Term | Meaning |
|---|---|
| Creation rule | A .sops.yaml entry mapping a path pattern to a KMS key. |
| Data key | The per-file key SOPS wraps with KMS; only the wrapped form is stored. |
| SecureString | A Parameter Store value encrypted at rest with KMS. |
| Syncer | The acme-secrets-syncer role the workflows assume. |
| Placeholder value | A literal that validate.sh refuses to sync. |
| Stage matrix | The per-stage sync jobs; a manual run narrows it to one stage. |
Where to read more
- Secrets Blueprint overview
- KMS for the per-stage keys
- Systems Manager Parameter Store for where values land
- External Secrets Operator for delivery to pods