SOPS + Age a Perfect Twin Brothers for Cost Effective Secret Management in Git
SOPS + Age a Perfect Twin Brothers for Cost Effective Secret Management in Git
Background
When I moved to my current company, I was shocked and a bit irritated by the current condition in the infrastructure side. The infrastructure was not well-organized and documented, there is no Infrastructure as Code (IaC) that I love so much.
I started to introduce and initiate the IaC, and in the process I found that I need to manage the secrets. At first, I want to use GCP Secret Manager / GCP KMS, like what I know in AWS, we can use AWS Parameter Store / AWS KMS to do encryption and storing secrets but it’s not cost effective at current scale and condition, we faced.
Racking my brain, when I on the progress of initiating and migrating some of our live Infrastructure to IaC, I found problem that I need to store secrets in git. reading here and there, I stumpled to sops, that well integrated with terragrunt. As terragrunt have sops_decrypt_file built in function. The later question that race in my mind is, what next tools I can use for the encryption part. As I mentioned, obiviously I will skip AWS KMS, GCP KMS, and Hashicorp Vault, since they need additional cost. So what left is gpg or age. Quick comparison, gpg is a bit complex and age is simple, easy to use, and maybe less computing resources needed, my opinionated hypothesis (not yet proven by data), as I stumbled As I found Age vs GPG #432. Other than that we don’t need beautiful exchange feature in GPG like what terraform aws provider does in pgp_key with keybase.io. So here its goes, I choose sops with age for the secrets data encryption before storing it in git.
How To
In this chance, I will focus on how to integreate sops with age to encrypt data then we can store it in git, and decrypt it when we need it.
brew install sops
brew install age
- Check if the installation is successful
$ sops --version
age --version
age-keygen --version
- After that, we need to generate age key pair, and let us check the content
age-keygen -o keys.txt
cat keys.txt
# created: 2024-02-07T19:44:54+07:00
# public key: age19z2w4gkykg5naumux8l50anh8veav5sk0wl4lmjx9gjggmrc74uq3w92uv
AGE-SECRET-KEY-1Y0HRFPHJMKEFQ2249ZR3LZMJ295J5W3VE9QYDTASWW7H9JT9LTAS2QQ88N
As you see the key pair will be :
The Public key is
age19z2w4gkykg5naumux8l50anh8veav5sk0wl4lmjx9gjggmrc74uq3w92uv
The Private key is
AGE-SECRET-KEY-1Y0HRFPHJMKEFQ2249ZR3LZMJ295J5W3VE9QYDTASWW7H9JT9LTAS2QQ88N
- Configure SOPS to use Age
# configure sops to use our private key ~/keys.txt
export SOPS_AGE_KEY_FILE=~/keys.txt
# configure sops to use our public key / recipient
export SOPS_AGE_RECIPIENTS=age19z2w4gkykg5naumux8l50anh8veav5sk0wl4lmjx9gjggmrc74uq3w92uv
- Now we can start to encrypt the data with SOPS and Age
lets say we have a file secrets.yaml
with the following content
secrets:
db:
username: admin
password: Str0nGP@ssw0rd
api:
key: 1234567890987654321
we can encrypt the file with the following command
sops --encrypt secrets.yaml > secrets.enc.yaml
- Check the encrypted file
cat secrets.enc.yaml
secrets:
db:
username: ENC[AES256_GCM,data:02kaPac=,iv:1Ixg5HTyMs/lIAAD1CPKB/DMQ9dGFB/zjtkCcn+j2ZA=,tag:2sBjBrxTZF4hXvxOAwnUOg==,type:str]
password: ENC[AES256_GCM,data:LftsUgh3Kr3hbF4UZkE=,iv:UCNXF47VD2szy9WEd4X5rUbvJTlxh8WaK16W5f9FYBc=,tag:TTlY83P1qllLrq1OjgJxlg==,type:str]
api:
key: ENC[AES256_GCM,data:j930xJxvTMS0EG4WDu5teC2p8A==,iv:TgRwnGI3uFBWXpmhkyhqZW2t83isJ+zNH6CB0OH8dLk=,tag:y1KioWxIj4LJJ3v7utEIAQ==,type:int]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age19z2w4gkykg5naumux8l50anh8veav5sk0wl4lmjx9gjggmrc74uq3w92uv
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSdmtQV0lxN3V5dStReXVW
dk9pZWJHYkwzMzhKYkZVRW1vQXZvMUtnWEdvCmZCbUJTZWlOZlRVeVlKclFGNFZB
U2NRS0F3NFItTjNMMDY0M2pKM1pTVTgKLS0tIGljSEpLRGo5UTVrK3czaWI2OVA1
OVJHRURsNzlHZFVoclRoZDVveEIzS1EKR/XGuFj8VybQf9u89vZGutiPzwT6uGXj
OYDPP13fyrZxlNi+1O+QcJ+JRYhPQKON1SpAXHR4etVUXRGWDalXMQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-02-07T12:58:57Z"
mac: ENC[AES256_GCM,data:5jMI76oTnUxgEUCWKpYjSbendNgWrNaolftrUWyH9gpDq86VYAL0tGF2sh7sM6qLOVQomcpwtpYjtmZxXgwojmi2jxzKbBMreAr9Dhip1LmYpRc/BMQw4aDufIfUPBPN3b2IQaVI98lyLJJYPnYUuuCJBDLVoZ2QrRYLKhmGoVk=,iv:X4uHt5nafPOt2NR0CU6OF7MXVyIMDnxwQo6dNSqJ+S8=,tag:RflCmG239NWtZQgAy65P8w==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.8.1
- Next we can try to decrypt the file
sops --decrypt secrets.enc.yaml > secrets.test.yaml
secrets:
db:
username: admin
password: Str0nGP@ssw0rd
api:
key: 1234567890987654321
- Now we can commit the
secrets.enc.yaml
to git, and when we need to decrypt it, we can use thesops
command to decrypt it.
Conclusion
In this post, I have shown how to integrate sops with age to encrypt data then we can store it in git, and decrypt it when we need it. This is a cost effective way to manage secrets in git, and it’s easy to use. As far as I know, this sops and age is well integrated with terragrunt, and argocd using helm-secrets. So it’s a good choice for managing secrets in git for your Infrastructure as Code (IaC), GitOps with ArgoCD, and maybe other use cases.