Easy git access control

Basic access to a git repository is fairly easy.
You specify --shared when initializing your bare repository, chgrp <your group> to the repository directory and your done. Now everyone in <your group> has read and write access to the repository with their account.

So what do I need?

  • An easy way to tell which user can actually write or not
  • A solution easy to integrate with my actual configuration manager - Here rudder/cfengine

Gitolite - to name the widely used tool for this kind of case - requires to install a bunch of script files, set a common git user, and store all its settings in a specific git repository. Definitely not practical to integrate in a configuration management tool.

How to do it then?

It is actually very easy to achieve these requirements with only basic linux settings and git hooks.

  • Read access:
    • ssh + user group
  • Write access:
    • git hook + acl file

Create your new git repository:

git init --bare --shared <repo>
chgrp -R <your group> <repo>
chmod o-rx <repo>
ls -ld <repo>
drwxrws--- 7 root <your group> 10 Oct 21 09:40 <repo>

Replace <repo>/hooks/update by the following python script.
This script checks the type of command you're sending and validates if your username is listed in the git.acl file and exits accordingly.

#!/bin/env python
import os, sys, subprocess, ConfigParser

refname = sys.argv[1]
oldrev = sys.argv[2]
newrev = sys.argv[3]
user = os.getenv('USER')

revtype = ['commit', 'delete', 'tag']

config = ConfigParser.RawConfigParser()
config.read('./git.acl')
users = config.get('users', 'write').split()


print refname
print "oldrev="+oldrev
print "newrev="+newrev
print user

cmd = 'git cat-file -t %s' % (newrev)
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=None, shell=True)
output = process.communicate()
thistype = output[0][:-1]

if ( thistype in revtype and not user in users):
    print "!!! You're not authorized to push code to this repo"
    sys.exit(1)

sys.exit(0)

Here is <repo>/hooks/git.acl

[users]
write = admin joe bob brad

The script could easily be extented to do more granular rights - allow pushing to branches but not to master, which branches a user could push to, etc... - while still being very easy to deploy using your favorite configuration management tool.