This is my revision of Polonskiy's Gist. It's basically a condensed version of the latter. Please refer to his version for further details, such as references which inspired the method proposed here.
When working with a remote git repository which is hosted on a
third-party storage server, data confidentiality sometimes becomes
a concern.
This gist walks you through the procedure of setting
up git repositories for which the local working directories keep behaving as normal (unencrypted) git repos, while all committed content gets encrypted.
This guide shows how to set up your Git repository so that, before data is pushed to the remote repository to store, it gets encrypted with an encryption key which is known only to the data owner itself. Management of the encryption key(s) and the encryption/decryption processes is always tedious and easy to get wrong. In the following, we shall demonstrate how to use Git with encryption in a way transparent to the end user.
The idea is to leverage git's smudge/clean filter by using OpenSSL's symmetric key cipher.
We thus need the following software packages to be installed:
git
,openssl
.
The method has been tested on Ubuntu 20.04.1 LTS.
Before creating the git repository, let's create a .gitencrypt
in our home directory
$ mkdir .gitencrypt
$ cd !$
and create three files in it
$ touch clean_filter_openssl smudge_filter_openssl diff_filter_openssl
$ chmod 755 *
These files will be the clean/smudge/diff handler/hook for the git repository which we are going to work with.
The first file is clean_filter_openssl
:
#!/bin/bash
SALT_FIXED=<your-salt> # 16 hex characters
PASS_FIXED=<your-passphrase>
openssl enc -base64 -aes-256-ecb -pbkdf2 -iter 100000 -S $SALT_FIXED -k $PASS_FIXED
Here replace <your-salt>
with a random hexadecimal string and replace
<your-passphrase>
with a passphrase you will use as a mater secret for
the symmetric key encryption/decryption. We are using AES-256 ECB mode
as the encryption algorithm.
You may replace it with another deterministic encryption algorithm (see Polonskiy's Gist for a discussion on this point).
The next file is smudge_filter_openssl
:
#!/bin/bash
# No salt is needed for decryption.
PASS_FIXED=<your-passphrase>
# If decryption fails, use `cat` instead.
# Error messages are redirected to /dev/null.
openssl enc -d -base64 -aes-256-ecb -pbkdf2 -iter 100000 -k $PASS_FIXED 2> /dev/null || cat
The last file is diff_filter_openssl
:
#!/bin/bash
# No salt is needed for decryption.
PASS_FIXED=<your-passphrase>
# Error messages are redirect to /dev/null.
openssl enc -d -base64 -aes-256-ecb -pbkdf2 -iter 100000 -k $PASS_FIXED -in "$1" 2> /dev/null || cat "$1"
Beware that your decryption passphrase is saved in plain text in the above files: you should thus make sure no untrusted party has access to your .gitencrypt directory.
Navigate to the project directory where the git repository has to be
created. Let such directory be ~/myproj/
.
Once there, initialize the repository
$ git init
Then, create a .gitattributes
file
$ touch .gitattributes
and paste the following content to it
* filter=openssl diff=openssl
[merge]
renormalize=true
The above lines in .gitattributes
are assigning the filter
and diff
attributes to drivers named openssl
, which should be defined in .git/config
as follows
[filter "openssl"]
smudge = ~/.gitencrypt/smudge_filter_openssl
clean = ~/.gitencrypt/clean_filter_openssl
[diff "openssl"]
textconv = ~/.gitencrypt/diff_filter_openssl
You can now git add
relevant files to the staging area. When you do that,
the clean
filter encrypts the files in your working directory before they are checked into the staging area.
(NB As a best practice, .gitattributes
should not be added.)
At this point,
you can use git diff
as usual, since the diff
filter is properly
configured to compare the difference of the plain text data only, and you can also apply git commit
to commit the changes to the repository.
Suppose the repository myproj
is connected to a remote repository
named Cloud at file://~/myproj-remote.git
, and you have pushed all
the committed changes to it. Let's also assume that you want to create another git
repository in directory ~/myproj-1
.
First clone the remote repository without checking out the HEAD.
$ git clone -o Cloud -n file://myproj-remote.git myproj-1
$ cd myproj-1
Now, create a folder .gitattributes
under myproj-1
with the same content
as shown in Step 2. Don't forget to also add/append the code snippet in .git/config
in
Step 2 to myproj-1/.git/config
.
We can then reset the HEAD
to check out all files
$ git reset --hard HEAD
When the files are checked out, the smudge
filter is automatically
applied, decrypting the files in the repository and putting the decrypted
files into your working directory.
From now on, you can work as usual in the local repositories and push/pull to/from the remote repository, without even noticing the encryption/decryption process in the background.
I think this is cool.
- Polonskiy