Skip to main content

Secret management with compose

Out of the box yeet provides no secret management by itself, instead we rely on providing environment variables via env files which we recommend are encrypted using something like git-crypt. There are many ways to encrypt files but we will explain a workflow using git-crypt here.

The short version

The short version of the workflow is that a .gitattributes file tells git-crypt which files to encrypt in a git repo. These files are transparently encrypted when committed and are therefore safe to put into a public git repo. However, if the aim of a public git repo is to share your configurations with the world then encrypting the entire contents of compose.yaml defeats that objective. Therefore we opt to encrypt an env vars file instead and then import those vars into the compose file on the remote host.

For example, take the following minimal compose.yaml example:

---
services:
librespeed:
image: lscr.io/linuxserver/librespeed
ports:
- 8008:80
environment:
- PASSWORD=${PASSWORD}

Pair it with an env file in the same directory as the compose.yaml itself which contains:

PASSWORD=password123

Docker will interpolate the values from the env file directly into your container at runtime.

The clever part comes when pairing this approach with git-crypt because now we can encrypt just the env file simply by naming it env.enc and creating a .gitattributes file with a pattern matching rule like so:

.gitattributes !filter !diff
**/*.enc filter=git-crypt diff=git-crypt
**/*.enc.* filter=git-crypt diff=git-crypt
**/*.enc/** filter=git-crypt diff=git-crypt

Copy your env.enc file across to your remote hosts yeet service directory with scp like so:

scp env.enc librespeed@yeethost:env

Then redeploy the service with:

yeet run librespeed librespeed/compose.yaml

If you'd like to verify the env vars are successfully picked up, run docker exec -it librespeed sh -c 'echo $PASSWORD' on the remote yeet host where the container is running.

git-crypt workflow

Here's a quick end to end guide for using git-crypt to perform the above.

Installation

You'll need to install git-crypt - full instructions are provided via the git-crypt GitHub repo. This guide continues once git-crypt is installed.

GPG keys

git-crypt relies on GPG keys and OpenPGP for encryption. If you don't have a GPG key already it is simple to create one - note you need the gpg toolchain installed :

gpg --full-gen-key

# answer the prompts as you wish or with following:
# 1) RSA and RSA
# keysize (default is 3072) - 4096
# key validity length - up to you
# GnuPG needs to construct a user ID to identify your key
# complete the prompts appropriately
#
# When happy with your selections, press O (Okay) to create the keypair

Verify the keys known to the system now includes your newly created key with:

gpg --list-keys

# /Users/alice/.gnupg/pubring.kbx
------------------------------
pub rsa4096/0xEB4F8DBF8DBF8DB3 2024-12-27 [SC]
Key fingerprint = F8DB F8DB F8DB F8DB F8DB F8DB F8DB F8DB F8DB 1463
uid [ultimate] alice <[email protected]>
sub rsa4096/0xF8DBF8DB07C2F8DB 2024-12-27 [E]

Configuration basics

Enter into your git repo directory. Make sure you are on the main/master branch, with no outstanding changes, initialize git-crypt, and add the key to repo. For example:

$ cd git-repo # we assume this is already an initialized git repo
$ git-crypt init
$ git-crypt add-gpg-user [email protected]

In the background git-crypt already committed a change (a new .gpg file in the .git-crypt directory), so now do:

git push

git-crypt encryption instructions

git-crypt is now set up and ready to be instructed which files to encrypt. The files you choose to encrypt are governed by the contents of the .gitattributes file.

The following example file will automatically encrypt any file which containers .enc somewhere in its filename. This could be secrets.enc or secrets.enc.yaml or even a directory named enc - they will be committed to GitHub as encrypted files all transparently to you. Note you can lock or unlock the files with git-crypt lock / unlock if you'd prefer files in your local working tree are encrypted too.

Put this file in the root of your git repo named .gitattributes. Note there is a separate .git-crypt directory containing a file named the same which should not be modified.

.gitattributes !filter !diff
**/*.enc filter=git-crypt diff=git-crypt
**/*.enc.* filter=git-crypt diff=git-crypt
**/*.enc/** filter=git-crypt diff=git-crypt

Commit this file to git. You can now create files with names which match the pattern in the .gitattributes file.

You might at first wonder if git-crypt is doing anything. But once you commit and push your .enc file it will automatically be encrypted and only viewable by someone with your PGP key (hopefully that's only you!). You'll be asked for a passphrase in addition to the key, so keep that safe too.

Check the status of any files you think should be matched by this rule with git-crypt status:

$ git-crypt status

encrypted: uptime-kuma/env.enc
not encrypted: ../../.gitattributes
...
not encrypted: ../../hosts.ini

Multiple machines

It's likely you'll want to access this git repo and yeet from multiple machines. You'll need to copy your GPG key over, or add that GPG identity to the repo. Export the private key (and store it with care!) using:

$ gpg --export-secret-key -a > secretkey.asc

Then copy that file wherever you need it and import it on the other end with

$ gpg --import secretkey.asc

Once the keyfiles have served their purose securely delete them with:

$ shred secretkey.asc
$ rm secretkey.asc

env file configuration

Create a file named env.enc in the root of the service directory alongside the compose file you used to deploy the service with yeet.

Copy your env.enc file across to your remote hosts yeet service directory with scp like so:

scp env.enc librespeed@yeethost:env

Then redeploy the service with:

yeet run librespeed librespeed/compose.yaml

If you'd like to verify the env vars are successfully picked up, run docker exec -it librespeed sh -c 'echo $PASSWORD' on the remote yeet host where the container is running.