CoursesSOPSAdvanced

SOPS in CI/CD

Decrypt safely in pipelines.

Advanced12 min · lesson 10 of 12

Outside GitOps, pipelines often need to decrypt SOPS secrets to deploy or configure something — and the goal is to do it without ever writing plaintext where it can leak. The pattern: give the CI job access to the decryption key via the platform’s secret store or, best, cloud OIDC (so the runner assumes a role with kms:Decrypt and no static key exists), then decrypt into the environment for the exact command that needs it using sops exec-env, avoiding a plaintext file on disk.

DECRYPTING SOPS SAFELY IN CI
1Job requests OIDC token
id-token: write, no static key
2Assume scoped role
only kms:Decrypt, short-lived creds
3sops exec-env
decrypt into env for one command
4Command runs
no plaintext file; values masked in logs
Short-lived, scoped, audited beats a stored key; keep plaintext out of logs and off disk.
.github/workflows/deploy.yml
jobs:
deploy:
permissions: { id-token: write, contents: read } # OIDC -> assume a role with kms:Decrypt
steps:
- uses: aws-actions/configure-aws-credentials@v4
with: { role-to-assume: arn:aws:iam::ACCT:role/ci-sops-decrypt, aws-region: us-east-1 }
- run: sops exec-env secrets.enc.yaml './deploy.sh' # decrypt into env for this command only

Keep plaintext transient and out of logs

Two rules make CI decryption safe. First, prefer KMS + OIDC over a committed or stored age key, so the pipeline has short-lived, scoped, audited decrypt access rather than a long-lived key that a compromised runner exfiltrates. Second, never let decrypted values reach logs or artifacts — use exec-env (not sops -d > file), avoid echoing secrets, and mark them masked in the CI system. The blast radius of a CI compromise is proportional to how much and how long plaintext exists in it.

A static decryption key in CI is a standing liability
Storing a long-lived age private key (or broad KMS credentials) in CI secrets means any compromise of the runner or the pipeline hands an attacker the ability to decrypt your secrets — durably. Use OIDC to assume a scoped, short-lived role with only kms:Decrypt on the needed key, decrypt only for the command that needs it, and keep plaintext out of logs and artifacts. Short-lived, scoped, audited beats a stored key every time.