Encryption with SOPS¶
Doco-CD supports the encryption of sensitive data in your doco-cd app config and deployment files with SOPS. age is the recommended key service, AWS KMS and other cloud key services work as well, see below.
How doco-cd detects and decrypts encrypted files¶
When a deployment is triggered, doco-cd decrypts every SOPS-encrypted file in the source revision it just fetched, before the stack is deployed. Decryption happens in doco-cd's own copy of the source, never in your repository.
Files and directories matched by .gitignore are skipped. The following directories are always excluded: .git, .github, .vscode, .idea, and node_modules.
An encrypted file whose content did not change since an earlier revision takes the plaintext of that revision, as long as it is still present in the artifact storage. The key service is therefore only called for files that actually changed, which keeps the cost of a cloud key service low.
Revoking a key does not take effect for unchanged files
A host that lost its key, or its access to the key service, still produces plaintext for files whose encrypted content did not change, from the copies it already holds. Re-encrypt the files, e.g. with sops rotate or sops updatekeys, so their content changes and the next revision needs the key again.
If doco-cd has no key for a file, it's left encrypted and logged instead of failing the entire deployment. This is intentional: a repository may legitimately contain secrets encrypted for someone else. If a deployment actually uses an encrypted file, the deployment fails at that point, naming the file.
Files a compose project references outside the repository (for example a bind mount pointing at an arbitrary host path) are decrypted as well, when the project is loaded. For bind-mounted directories, all files inside are scanned recursively.
Detection is content-based: a file is treated as SOPS-encrypted if its content contains both sops and ENC[. No special file naming convention is required.
The format used for decryption is determined by file extension:
Supported file formats¶
SOPS supports files in the following formats:
| Format | Required file extension | Example |
|---|---|---|
| YAML | .yaml or .yml | example.yaml |
| JSON | .json | example.json |
| Dotenv | .env | example.env |
| INI | .ini | example.ini |
| Binary/Text | any other or noneFallback/Default format | example.txtexample (no extension) |
Getting the extension wrong won't prevent detection, but it will cause decryption to fail, so make sure encrypted files have the correct extension for their format.
Usage with SOPS and age¶
I recommend to use SOPS with age for encrypting your deployment files.
For this, you need to
- Install age on your system
- Create an age key pair.
- Encrypt your files with SOPS using the age public key, see SOPS: Encrypting using age.
-
Set one of the following environment variables below for doco-cd to use the age key with SOPS:
Key Type Description SOPS_AGE_KEYstring The age secret key (See the SOPS docs) SOPS_AGE_KEY_FILEstring The path inside the container to the file containing the age secret key (e.g. /sops_age_key.txt)I recommend using the
SOPS_AGE_KEY_FILEenvironment variable and mount the age secret key as a Docker secret. See the example below for how to do this.Info
For all available SOPS environment variables and configuration options, see the SOPS documentation.
-
When triggering a deployment, doco-cd will automatically detect the SOPS-encrypted files and decrypt them using the provided age key.
It is important that you give your files the correct file extension, so that the correct file format is used during the decryption process.
Tip
You can also encrypt only parts of a file and keep the rest in plaintext. See Encrypting only parts of a file in the SOPS docs for more information.
Usage with SOPS and cloud key services¶
doco-cd uses the SOPS Go library, so decryption is not limited to age. SOPS reads the key metadata from each encrypted file and picks the matching key service, doco-cd needs no configuration for this. What matters is how the credentials for that key service reach the doco-cd container: only environment variables, mounted files and the instance metadata service of the cloud VM are available. There is no interactive login (aws sso login, gcloud auth, az login) inside the container.
At least one SOPS_* environment variable must be set
doco-cd only decrypts when a non-empty environment variable starting with SOPS_ is set. Cloud key services don't need one for decryption itself, so set one as a marker, e.g. SOPS_KMS_ARN. Without it every deployment that uses an encrypted file fails with SOPS secret key is not set.
| Key service | Credentials for doco-cd | Notes |
|---|---|---|
| age | SOPS_AGE_KEY or SOPS_AGE_KEY_FILE | See above |
| AWS KMS | EC2 instance profile via IMDS, or AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY, or a mounted credentials file via AWS_SHARED_CREDENTIALS_FILE | AWS_REGION is required, see example below. Verified. |
| GCP KMS | Service account attached to the VM via the metadata server, or a service account key via GOOGLE_CREDENTIALS (JSON content or path) / GOOGLE_APPLICATION_CREDENTIALS (path) | Not verified with doco-cd, follows from the SOPS credential chain |
| Azure Key Vault | Managed identity of the VM via IMDS, or a service principal via AZURE_TENANT_ID + AZURE_CLIENT_ID + AZURE_CLIENT_SECRET | Not verified with doco-cd, follows from the SOPS credential chain |
| HashiCorp Vault | VAULT_TOKEN (the Vault address is stored in the encrypted file) | Token must be long-lived or renewed outside doco-cd. Not verified with doco-cd. |
| PGP | Legacy secring.gpg with an unprotected key, mounted under GNUPGHOME | Not recommended: the image has no gpg binary, so only the pure Go OpenPGP path works |
Several recipients on one file are fine, e.g. KMS for humans and CI plus an age key for doco-cd.
Example with AWS KMS¶
- Encrypt your files with SOPS using the KMS key:
- Give the doco-cd host credentials that allow
kms:Decrypton that key. An EC2 instance profile (IAM role) is the least effort, static credentials viaAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYwork as well. -
Set the environment variables:
docker-compose.ymlservices: app: environment: AWS_REGION: eu-central-1 # (1)! SOPS_KMS_ARN: arn:aws:kms:eu-central-1:123456789012:key/<key_id> # (2)!- Required. The AWS SDK doesn't discover the region from instance metadata.
- Only the marker described above, the actual key ARN comes from the encrypted file.
Instance profile from inside a container
The container reaches the instance metadata service through the Docker bridge network, which is one extra network hop. Set the hop limit of the instance to 2, otherwise every decryption fails with a credentials error:
Example setup with SOPS and age¶
Doco-CD configuration¶
Example of a docker-compose.yml file using SOPS with age:
Use the docker-compose.yml as the base reference and add the following lines to it:
services:
app:
environment:
SOPS_AGE_KEY_FILE: /run/secrets/sops_age_key # (1)!
secrets:
- sops_age_key
secrets:
sops_age_key:
file: sops_age_key.txt
- Docker Secrets are always mounted in the
/run/secrets/directory if no target is specified
App configuration with SOPS-encrypted values¶
To use encrypted values in the doco-cd app configuration, store secrets in encrypted text files and reference them with *_FILE environment variables (for example, GIT_ACCESS_TOKEN_FILE). Each variable should point to the encrypted file path inside the container.
Encrypted Git access token
To use an encrypted Git access token, create a text file with the token and encrypt it with SOPS:
printf "my-git-access-token" > git-access-token.txt
sops encrypt --age age1g3lcl... git-access-token.txt > git-access-token.enc.txt
Then set the GIT_ACCESS_TOKEN_FILE environment variable in your docker-compose.yml file to the encrypted file path:
Deployment with a SOPS-encrypted file¶
First, I use my age public key from the previously generated key pair to encrypt my secrets.env file:
Generate the encrypted file with SOPS:
Then, I set the encrypted file in my docker-compose.yml file:
When I now trigger a deployment, doco-cd will automatically decrypt the secrets.enc.env file using the provided age key and deploy the container with the environment variables in it.