Imagine this: you are working on a project but must store sensitive information. In my case, the Secret Key of my Cloud provider. Imagine all the things that an attacker could do with that Secret Key. It’d leave me in bankruptcy. The attacker could add how many resources they want, add more CPU to my machines, disk space, increase the size of the machine to the more advanced types, etc. Guess what could happen at the end of the month…

This case never happened to me, but it is a possible scenario if you expose your sensitive information in your Version Control System. It does not matter what kind of sensitive information you have (API Keys, M2M keys, passwords, etc.); you should (or have?) exclude them from your VCS and securely handle them. In this article, I’m going to publish my approach and the tools that I use:

The perfect combination (for me):

SOPS + age

Glossary:

VCS: Version Control System.

I’m not going to explain in detail what SOPS and age are; you can check its documentation. But briefly:

  1. SOPS is a CLI tool to encrypt and decrypt data. That’s it.
  2. age is an encryption tool. It generates a public key and a private key that SOPS requires.

Ok, too much talk… Let’s work!

1. Install SOPS and age

(I’m assuming you are on Mac, but if you are on Windows or Linux, use your OS’s package manager.)

brew install sops age

2. Generate the key with age

age-keygen -o key.txt

Now that you have a key.txt, check it out. It should look like:

# created: 2024-07-13T09:45:51+02:00
# public key: [agethe-public-key-generated-by-age]
AGE-SECRET-KEY-[a-secret-key-generated-by-age]

2. Configure a .sops file

Although we can pass all the configurations we will write in this file through the CLI, it is tedious and not versionable. That’s why SOPS can handle all its configuration with a .sops file. Mine looks like this:

File: .sops.yaml
creation_rules:
  - path_regex: /*secrets(\.encrypted)?.yaml$
    age: agethe-public-key-generated-by-age

Where:

  1. patch_regex is the path where SOPS will look for your encrypted file. In my case, I store my encrypted secrets in a file called secrets.encrypted.yamls

  2. age This is your public key generated by age in step #2.

It is safe to push this file to your VCS.

3. Encrypting a file

SOPS supports JSON, YAML, and binaries, among other formats, as inputs for your sensitive data. I choose YAML. My file that contains the sensitive information looks like this:

File: secrets.yaml
aws_api_key: james
ssh_secret_key: rodriguez
a_password: my_password_xyz

Now, let’s encrypt it:

sops --encrypt secrets.yaml > secrets.encrypted.yaml

sops will know that you are using age, because of the configuration in .sops.yaml

It’ll generate a secrets.encrypted.yaml that will look like something like this:

aws_api_key: ENC[AES256_GCM,data:blahblahtag:gJV4VqWNQVT4Srv88oJJUQ==,type:str]
aws_api_key: ENC[AES256_GCM,data:blahblahtag:gJV4VqWNQVT4Srv88oJJUQ==,type:str]
a_password: ENC[AES256_GCM,data:blahblahtag:gJV4VqWNQVT4Srv88oJJUQ==,type:str]

sops:
 kms: []
 gcp_kms: []
 azure_kv: []
 hc_vault: []
 age:
 - recipient: agethe-public-key-generated-by-age
 enc: |
 -----BEGIN AGE ENCRYPTED FILE-----
 ...
 -----END AGE ENCRYPTED FILE-----
 lastmodified: "2024-07-13T14:30:06Z"
 mac: ENC[AES256_GCM,data:...,tag:43WAnpwgLkVmhPBjKapnsQ==,type:str]
 pgp: []
 unencrypted_suffix: _unencrypted
 version: 3.7.3

4. Decrypting a file

SOPS needs to know the path where you have stored your private key generated in step #1 with age. SOPS will look for this file with the path in the environment variable SOPS_AGE_KEY_FILE.

export SOPS_AGE_KEY_FILE="$(PWD)/key.txt" && \
sops --decrypt secrets.encrypted.yaml

And boom 💫💫 ! You get the output of the decrypted data in your console:

aws_api_key: james
ssh_secret_key: rodriguez
a_password: my_password_xyz

To take into account

SOPS and age have more configuration options like key_groups to handle multiple key management tools. You can check all its configuration and options in its official documentation. This article refers to my own recipe. Use it or modify it according to your needs.