Skip to content

Instantly share code, notes, and snippets.

@natema
Forked from polonskiy/encrypted-git-repo.md
Last active December 31, 2020 20:44
Show Gist options
  • Save natema/55c2ef2be4e63d480d8b5b61afa2c68a to your computer and use it in GitHub Desktop.
Save natema/55c2ef2be4e63d480d8b5b61afa2c68a to your computer and use it in GitHub Desktop.
Transparent Git Encryption

Transparent Git Encryption

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.

Description

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.

Rationale

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.

Setup

Step 1

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.

Step 2

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

Step 3

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.

Cloning the repo

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.

Conclusions

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment