Trac and git-http-backend

Here is how to setup a git repository to be used with Trac and served only by HTTP.

Prerequisites

First install the required software. On Debian Squeeze just do:

apt-get install git trac-git gitweb

Trac configuration

Once the Trac project has been created modify the trac.ini file to enable the git plugin:

[git]
#comment/remove git_bin:
#git_bin = git
#or give the full path
#git_bin = /usr/bin/git

[components]
tracext.git.* = enabled

[trac]
repository_dir = /srv/scm/git/dagobah.git
repository_type = git

Git repository creation

As easy as:

mkdir /srv/scm/git/dagobah.git/
cd /srv/scm/git/dagobah.git/
git init --bare
git config http.getanyfile true
git config http.uploadpack true
git config http.receivepack true

Apache and git-http-backend

git-http-backend doesn’t have any builtin access control mecanism. It relies on Apache for that. Having different ACL for different git repository is probably possible with only Apache configuration but it’s beyond my field of expertise. So I wrote a wrapper around git-http-backend to manage ACLs and also, to dispatch requests between git-web and git-http-backend:

#!/usr/bin/python

GIT_PROJECT_ROOT='/srv/scm/git'
GIT_HTTP_BACKEND='/usr/lib/git-core/git-http-backend'
GIT_WEB='/usr/share/gitweb/index.cgi'
GITWEB_CONFIG='/etc/gitweb.conf'

import os
import subprocess
import csv
import re

#I find easier to change git-http-backend settings here than in the
#apache config file but both work
os.environ['GIT_PROJECT_ROOT']=GIT_PROJECT_ROOT
os.environ['GIT_HTTP_EXPORT_ALL']="1"
os.environ['GITWEB_CONFIG']=GITWEB_CONFIG

def print_env():
    """For debugging"""
    print "Content-type: text/html\n"
    e = os.environ
    for n in e:
       print n + " " + e[n] +"\n"

user = os.environ.get('REMOTE_USER')
script_name = os.environ.get('SCRIPT_NAME')
path_info = os.environ.get('REQUEST_URI')
path_info = path_info[ len(script_name): ]

is_gitweb = re.search(r"^/[^/]*/(HEAD|info/refs.*|objects/info/[^/]+|git-(upload|receive)-pack)$", path_info) == None

if is_gitweb:
    match = re.search(r"\?p=([^;]*)", path_info)
    if match :
        project_name = match.group(1)
    else:
        project_name = path_info.split("/")[1]
    is_writing = False
else:
    project_name = path_info.split("/")[1]
    is_writing = path_info.endswith("git-receive-pack")

valid_user = False
try:
    acl_file="/".join([GIT_PROJECT_ROOT,project_name,"acl.conf"])
    reader = csv.reader(open(acl_file, "rb"), delimiter=' ')
    for l in reader:
        if len(l)==2 and (user == l[0] or l[0]=='*') and ( (l[1] == 'r' and not is_writing) or l[1]=='rw'):
            valid_user = True
            break
except IOError:
    pass

if valid_user:
    if is_gitweb:
        subprocess.call([GIT_WEB])
    else:
        subprocess.call([GIT_HTTP_BACKEND])
else:
    print "Content-type: text/html"
    print "Status: 403 Forbidden\n"
    print "The user "+str(user)+" is not allowed to access to this page."

This script read ACLs from a file named &ltgit repository&gt/acl.conf. The format is simple and allows to specify read or read/write access:

$cat /srv/scm/git/dagobah.git/acl.conf
yoda rw
leia r
r2d2 rw
obiwan r

To use this wrapper just add this line to the Apache configuration:

ScriptAlias /git /var/www/cgi/git-http-backend-wrapper.cgi

Ensure that the directory containing the cgi file have the +ExecCGI option.

References

#git, #trac