Encrypting secrets in a Git repository using git-crypt

Feb 8, 2018 · 4-minute read

git-crypt provides a way to encrypt a subset of files in a Git repository such that only specific collaborators can decrypt them, while making it possible for everyone else to access the unencrypted files and perform standard Git operations on the repository. At Hypothesis we use this to store various secrets (API keys, passwords) in a “playbook” repository for use by developers involved in the operation of the web service, while making it possible for a wider audience of staff at the organisation to access and update other resources in the same repository.

git-crypt’s encryption is “transparent” which means that selected files or directories are automatically encrypted when committed to the repository and decrypted when checked out. This is built on top of Git’s support for defining custom filter commands that are run on files as they are stored into or checked out from the repository. There are some limitations arising from this implementation which you should understand before choosing to use git-crypt. For example file names, commit messages and other metadata are not encrypted, only file content.

git-crypt operates by encrypting a subset of files in a repository using gpg. The files are encrypted with AES, using a key which is shared by the collaborators who have access to the encrypted files. The symmetric AES key is in turn encrypted using the public key of each collaborator and stored in a hidden git-crypt data folder. In the current version of git-crypt the encrypted keys are stored in .git-crypt/keys/{key name}/{version}/{public key fingerprint}.gpg.

Setting up git-crypt

The git-crypt documentation explains the process of setting up git-crypt in a repo, which I won’t repeat here.

Adding collaborators

Before adding a new collaborator, they will need to generate a GPG key. GitHub has good instructions for doing this. keybase.io is also a useful service for managing GPG keys online and verifying their authenticity by proving that a GPG key’s owner also owns specific online accounts.

When a new collaborator is added to the repository by an existing collaborator, the shared AES key is encrypted using the new collaborator’s public key and added to .git-crypt/keys. The add-gpg-user command is used to do this:

# Import the new collaborator's public key. Here `newdev.asc` is an ASCII-format
# PGP public key with a user ID of "newdev@org.com"
gpg --import newdev.asc

# Sign the key to verify that we trust it.
# See https://github.com/AGWA/git-crypt/issues/23#issuecomment-48412479
gpg --lsign-key newdev@org.com

# Encrypt the repository's shared AES key using the new collaborator's public key.
git crypt add-gpg-user newdev@org.com

# Share the updated `.git-crypt` dir so the new collaborator can decrypt encrypted files.
git push

Listing collaborators

As of v0.6.0, git crypt does not have a documented command to list information about who has access to secrets in the repository. However there are a couple of other ways to get this information.

The git history provides details of collaborators via the commits created by git-crypt add-gpg-user and other commands. To list only commits affecting git-crypt data, use:

git log .git-crypt/

You can also list the fingerprints of public keys that currently have access to files encrypted with a particular key by using gpg’s --list-packets command, and then filtering the resulting output to include only the key IDs:

find .git-crypt/keys/{key name}/{version}/ -type f | xargs  -I '{}' gpg --quiet --batch --list-packets --list-only '{}' | egrep -o 'keyid [A-F0-9]+'

Removing collaborators

Since all encrypted files are encrypted with the same shared symmetric key, removing a collaborator involves several steps:

  1. Removing the ex-collaborator’s encrypted copy of the symmetric key from the git-crypt data directory. Note that this copy will still be present in the git history and the collaborator may have a decrypted copy of the key on their own system.
  2. Re-encrypting the encrypted files with a new shared key.
  3. Re-encrypting the shared key with the public key of remaining collaborators.

It is important to note that these steps only remove access to future updates to encrypted data in the repository. The ex-collaborator will still be able to retrieve past versions of encrypted data and therefore any secrets that were stored encrypted (eg. API keys, passwords) will need to be rotated to protect those services.

Unfortunately git-crypt lacks a command to remove a collaborator at present. See this GitHub issue for various solutions.

Further reading

Andrew Ayer’s project page for git-crypt.