I have a private GitHub repository that I keep all my config files in. Realistically, a private GitHub repo is probably enough for personal security and I could commit secret things configs into this repo unencrypted. But if you don’t want to trust GitHub with your secret data, or if you want to push your config files to a bare repo hosted somewhere less private and secure, or check them out somewhere insecure, then it’d be a good idea to encrypt any secret data before committing it to git.

git-crypt is a git extension that can transparently encrypt files before they get committed to git, and decrypt them after they get checked out from git. You tell it which files to encrypt using .gitattributes files, so you can mix encrypted and unencrypted files in the same git repo. It will even store encrypted diffs when you change encrypted files, so you can still see line-by-line diffs of encrypted content with git log -p.

To install git-crypt on Ubuntu 15.10+ just do sudo apt-get install git-crypt, on Ubuntu < 15.10 you have to install it from source:

sudo apt-get install -y build-essential libssl-dev
git clone https://github.com/AGWA/git-crypt.git
cd git-crypt
make
sudo make install

To enable git-crypt on a git repo cd into the repo, init git crypt, and add your GPG key:

git crypt init
git crypt add-gpg-user <KEY ID>

(To get your GPG key ID run gpg --list-keys, you can add as many GPG keys as you want this way if you want to share the encrypted contents with others.)

After git cloning a repo with git-crypt files in it you need to decrypt it by running:

git crypt unlock

From now on git-crypt will transparently encrypt and decrypt files for you when you commit and check them out as normal.

You now need to add .gitattributes files to tell git-crypt which files to encrypt. It’s easy to accidentally commit unencrypted files that you thought were going to be encrypted, by having them not match .gitattributes globs that you thought they would match. For that reason it’s best to use the top-level .gitattributes file to encrypt top-level files only, and give any subdirs with encrypted files their own .gitattributes files. Here’s my top-level .gitattributes file that encrypts my _msmtprc and _transifexrc files, as well as all _mutt.password.* and _offlineimap.passwords.* files:

_msmtprc filter=git-crypt diff=git-crypt
_mutt.password.* filter=git-crypt diff=git-crypt
_offlineimap.passwords.* filter=git-crypt diff=git-crypt
_transifexrc filter=git-crypt diff=git-crypt

And here’s my secret/.gitattributes file that encrypts everything in the secret subdir using *:

* filter=git-crypt diff=git-crypt
.git* !filter !diff
README !filter !diff

Notice the additional rules to prevent files like .gitignore and the .gitattributes files, and any README file, from being encrypted. You can copy this .gitattributes file into any directory that should be encrypted.

Make sure you commit the .gitattributes files into the git repo.

Before committing secret files, run git crypt status to check which files are encrypted and which aren’t. You can get a list of all files that will be encrypted before commit with:

git crypt status | ack '    encrypted'

Or list the encrypted or unencrypted status of all files in the _ssh directory with:

git crypt status | ack '_ssh'

You want to check for both files that should be encrypted but aren’t, and also files that should not be encrypted but are (including .gitattributes and .gitignore files in encrypted subdirectories - these should not be encrypted).

Purging unencrypted sensitive content

If you accidentally commit some unencrypted sensitive content that should have been encrypted, it’s not enough to just roll back the branch as the commit will still be in your .git directory (and will still propagate into the .git directories of any clones).

You can use BFG Repo-Cleaner to clean out sensitive content. To install it:

sudo apt-get install openjdk-7-jre
wget 'http://repo1.maven.org/maven2/com/madgag/bfg/1.12.5/bfg-1.12.5.jar'

It’s a good idea to backup your repo before using BFG on it, in case you accidentally delete something you didn’t want to:

git clone --mirror /path/to/your/repo /path/to/your/repo.backup

Then, for example to purge the _ssh directory:

java -jar bfg-1.12.5.jar --delete-folders '_ssh' .git
git reflog expire --expire=now --all
git gc --prune=now --aggressive