[nginx] configure gitosis and basic http auth for git-http-backend
2. Path gitosis:
git am --signoff < feauture_http_auth.patch
From 815cf347c83465fb9fe20977198e12b9d438b014 Mon Sep 17 00:00:00 2001 From: Arkady Smirnov <smirnov.arkady@gmail.com> Date: Sat, 2 Jul 2011 20:35:28 +0300 Subject: [PATCH] Add http-auth feature --- gitosis/httpauth.py | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++ gitosis/run_hook.py | 5 ++ 2 files changed, 124 insertions(+), 0 deletions(-) create mode 100644 gitosis/httpauth.py diff --git a/gitosis/httpauth.py b/gitosis/httpauth.py new file mode 100644 index 0000000..eda96ff --- /dev/null +++ b/gitosis/httpauth.py @@ -0,0 +1,119 @@ +import os, errno, re +import logging + +log = logging.getLogger('gitosis.httpautch') + +from gitosis import util +from gitosis import access + +_ACCEPTABLE_USER_RE = re.compile(r'^[a-zA-Z][a-zA-Z0-9_.-]*(@[a-zA-Z][a-zA-Z0-9.-]*)?$') + +def isSafeUsername(user): + match = _ACCEPTABLE_USER_RE.match(user) + return (match is not None) + +def _extract_reldir(topdir, dirpath): + if topdir == dirpath: + return '.' + prefix = topdir + '/' + assert dirpath.startswith(prefix) + reldir = dirpath[len(prefix):] + return reldir + +def WriteHtpasswd(config, keydir): + repositories = util.getRepositoryDir(config) + + def _error(e): + if e.errno == errno.ENOENT: + pass + else: + raise e + for (dirpath, dirnames, filenames) \ + in os.walk(repositories, onerror=_error): + + reldir = _extract_reldir( + topdir=repositories, + dirpath=dirpath, + ) + + log.debug('Walking %r, seeing %r', reldir, dirnames) + to_recurse = [] + repos = [] + for dirname in dirnames: + if dirname.endswith('.git'): + repos.append(dirname) + else: + to_recurse.append(dirname) + dirnames[:] = to_recurse + + for repo in repos: + reponame, ext = os.path.splitext(repo) + if reldir != '.': + reponame = os.path.join(reldir, reponame) + assert ext == '.git' + + log.debug('http-auth for repo %r', repo) + + htpasswd_w = os.path.join(dirpath, repo, '.htpasswd-write') + htpasswd_r = os.path.join(dirpath, repo, '.htpasswd-read') + tmpw = '%s.%d.tmp' % (htpasswd_w, os.getpid()) + tmpr = '%s.%d.tmp' % (htpasswd_r, os.getpid()) + + try: + outw = file(tmpw, 'w') + outr = file(tmpr, 'w') + + + for filename in os.listdir(keydir): + if filename.startswith('.'): + continue + + username, ext = os.path.splitext(filename) + if ext != '.htpasswd': + continue + + if not isSafeUsername(username): + log.warn('Unsafe HTTP-AUTH username in keyfile: %r', filename) + continue + + path = os.path.join(keydir, filename) + f = file(path) + log.debug('OPEN %r', path) + + # write access is always sufficient + newpath = access.haveAccess( + config=config, + user=username, + mode='writable', + path=reponame) + + if newpath is None: + newpath = access.haveAccess( + config=config, + user=username, + mode='readonly', + path=reponame) + + if newpath is not None: + log.debug('USERNAME = %r has readonly access', username) + for line in f: + line = line.rstrip('\n') + print >>outr, line + else: + log.debug('USERNAME = %r has write access', username) + for line in f: + line = line.rstrip('\n') + print >>outw, line + print >>outr, line + + f.close() + os.fsync(outw) + os.fsync(outr) + + finally: + outw.close() + outr.close() + + os.rename(tmpw, htpasswd_w) + os.rename(tmpr, htpasswd_r) + diff --git a/gitosis/run_hook.py b/gitosis/run_hook.py index e535e6a..f8d089b 100644 --- a/gitosis/run_hook.py +++ b/gitosis/run_hook.py @@ -10,6 +10,7 @@ import shutil from gitosis import repository from gitosis import ssh +from gitosis import httpauth from gitosis import gitweb from gitosis import gitdaemon from gitosis import app @@ -47,6 +48,10 @@ def post_update(cfg, git_dir): path=authorized_keys, keydir=os.path.join(export, 'keydir'), ) + httpauth.WriteHtpasswd( + config=cfg, + keydir=os.path.join(export, 'keydir'), + ) class Main(app.App): def create_parser(self): -- 1.7.0.4
3. Nginx config
server {
listen 80;
server_name git.example.com;
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
client_max_body_size 500m;
proxy_buffering off;
# Enable chunk support
# https://github.com/agentzh/chunkin-nginx-module
chunkin on;
error_page 411 = @my_411_error;
location @my_411_error {
chunkin_resume;
}
set $git_root /home/git/repositories;
if ($uri ~ "^(/[^/]+\.git)(.*)" ) {
set $git_project $git_root$1;
set $git_export_ok $git_project/git-daemon-export-ok;
}
if ( !-d $git_project ) {
return 401;
}
if ($arg_service ~* "git-receive-pack") {
rewrite ^ /git-auth/write$uri last;
}
if ( -f $git_export_ok ) {
rewrite ^ /git/public$uri last;
}
# default auth-read action
rewrite ^ /git-auth/read$uri last;
location ~ ^/git/public(/.*) {
internal;
# fcgiwrap is set up to listen on this host:port
fastcgi_pass unix:/var/run/git-backend-fcgi;
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
#fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param GIT_PROJECT_ROOT $git_root;
fastcgi_param PATH_INFO $1;
fastcgi_param REMOTE_USER $remote_user;
include fastcgi_params;
}
location ~ ^/git-auth/read(/.*) {
internal;
auth_basic "git-auth";
auth_basic_user_file $git_project/.htpasswd-read;
# fcgiwrap is set up to listen on this host:port
fastcgi_pass unix:/var/run/git-backend-fcgi;
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param GIT_PROJECT_ROOT $git_root;
fastcgi_param PATH_INFO $1;
fastcgi_param REMOTE_USER $remote_user;
include fastcgi_params;
}
location ~ ^/git-auth/write(/.*) {
internal;
auth_basic "git-auth";
auth_basic_user_file $git_project/.htpasswd-write;
# fcgiwrap is set up to listen on this host:port
fastcgi_pass unix:/var/run/git-backend-fcgi;
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param GIT_PROJECT_ROOT $git_root;
fastcgi_param PATH_INFO $1;
fastcgi_param REMOTE_USER $remote_user;
include fastcgi_params;
}
}