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