nginx, cgit and git-http-backend

Sounds simple, right. Plug in cgit and git-http-backend with nginx to get nice web interface and working clone URL. And pushable too, of course. It turned out not to be quite that easy, but seems doable with some quirks.

There are plenty of instructions for parts of this lying around, but didn't find one that catches 'em all, so needed to some cuttin', pastin' and retryin'. The end result nginx configuration:

location ~ "(?x)^/git(?<path>/.*/(?:HEAD '
                             info/refs '
                             objects/(?:info/[^/]+ '
                                        [0-9a-f]{2}/[0-9a-f]{38} '
                                        pack/pack-[0-9a-f]{40}\.(?:pack '
                                                                   idx)) '
                             git-upload-pack))$" {
        error_page 491 = @auth;
        if ($query_string = service=git-receive-pack) {
                return 491;
        }
        client_max_body_size                    0;

        fastcgi_param   SCRIPT_FILENAME         /usr/lib/git-core/git-http-backend;
        include         fastcgi_params;
        fastcgi_param   GIT_HTTP_EXPORT_ALL     "";
        fastcgi_param   GIT_PROJECT_ROOT        /srv/git;
        fastcgi_param   PATH_INFO               $path;

        fastcgi_param   REMOTE_USER             $remote_user;
        fastcgi_pass    unix:/var/run/fcgiwrap.socket;
}
location ~ "^/git(?<path>/.*/git-receive-pack)$" {
        error_page 491 = @auth;
        return 491;
}
location @auth {
        auth_basic            "Git write access";
        auth_basic_user_file  /srv/git/.htpasswd;

        client_max_body_size                    0;

        fastcgi_param   SCRIPT_FILENAME         /usr/lib/git-core/git-http-backend;
        include         fastcgi_params;
        fastcgi_param   GIT_HTTP_EXPORT_ALL     "";
        fastcgi_param   GIT_PROJECT_ROOT        /srv/git;
        fastcgi_param   PATH_INFO               $path;

        fastcgi_param   REMOTE_USER             $remote_user;
        fastcgi_pass    unix:/var/run/fcgiwrap.socket;
}
location ~ ^/git(?<path>/.*)$ {
        alias /usr/share/cgit;
        try_files $1 @cgit;
}
location @cgit {
        include         fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
        fastcgi_param   PATH_INFO       $path;
        fastcgi_param   QUERY_STRING    $args;
        fastcgi_param   HTTP_HOST       $server_name;

        fastcgi_param   CGIT_CONFIG     /srv/git/.cgitrc;

        fastcgi_pass    unix:/var/run/fcgiwrap.socket;
}

cgit also requires configuration, it could be done system wide with /etc/cgitrc, but I opted defining CGIT_CONFIG environment variable to point to a custom path, the .cgitrc ended up like something like this:

css=/git/cgit.css
logo=/git/cgit.png

virtual-root=/git
clone-url=https://$HTTP_HOST/git/$CGIT_REPO_URL
scan-path=/srv/git

The binary and socket paths, and cgit data path, are those used by Debian default configuration, may need adjustment for different installations. Tried to get rid of the virtual-root directive in cgitrc, but that would require setting SCRIPT_PATH, which fcgiwrap eats away.

For more access control, you could grab the repository name from the request paths: "(?<path>/(?<repo>.*)/", and integrate the $repo into auth_basic_user_file.