2014-04-01: fixed a bug in section git-http-backend
There’s a lot of tutorials on setting up gitweb; even one here written by someone from the same organization.
Like with most sysadmin tasks, nothing quite works exactly out of the box as you’d like. Hopefully this will help someone else out there.
requirements
We want to provide anonymous, read-only access to git repository on a site and per-user basis. (Protected, read-only access is another article. Read/write access is probably best done with gitlab or gitolite.)
There are two types of repositories we have:
- a organization-wide git repository like https://ant.isi.edu/git/
- a per-user git repository like https://ant.isi.edu/git/~calvin/
This allows users to host as many of their own personal git repositories without cluttering the organization’s repository.
getting started
We use Fedora 20 (as of this writing), git 1.8.5.3, and Apache 2.4
(important, some ways of doing things in Apache 2.2 and earlier
have been deprecated) with mod_alias
and mod_rewrite
.
The following documentation is extremely helpful:
- gitweb(1) manual page
- git-http-backend(1) manual page
- gitweb.conf(5) manual page
- apache 2.4 mod_rewrite
- notes on upgrading to apache 2.4
installation and testing
If you haven’t installed Apache w/ SSL and git, do so now.
I live dangerously and do all my configuration testing on a production
machine.
If you feel inclined to do the same, continue reading.
Writing rewrite rules can be kind of painful, so I wanted to get the
most detail (the rest at warn
) in ssl.conf
.
Change the LogLevel
to however you see fit:
LogLevel warn rewrite:trace8
Then to sanity check a configuration file and reload the daemon:
apachectl configtest && service httpd reload
Install gitweb
:
yum install gitweb
There are are a few things that gitweb
will install.
/etc/gitweb.conf
: a perl script / configuration file for gitweb/etc/httpd/conf.d/git.conf
: apache configuration/var/www/git
: wheregitweb.cgi
and support files are located
We’ll use the default locations; adjust for your own setup.
Furthermore, we have 2 locations for repositories:
/nfs/git_public/
/home/$user/public_git/
where $user
is some valid user on a shared system.
gitweb
Getting gitweb up and running is relatively simple. Our configuration files will do the following:
- make gitweb accessible at
https://hostname/git
- access gitweb repositories via
https://hostname/git/repo.git
Our git.conf
for httpd:
Alias /git /var/www/git
# Redirect http:// to https://
RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule ^/git.*$ https://%{HTTP_HOST}%{REQUEST_URI}
<Directory /var/www/git>
SSLRequireSSL
Require all granted
Options +ExecCGI
# Run .cgi files using mod_cgi
AddHandler cgi-script .cgi
# Use gitweb.cgi for directory listings. This takes
# care of requests to /git and /git/
DirectoryIndex gitweb.cgi
# Set various environment variables, some to pass to
# gitweb
SetEnv GITWEB_CONFIG /etc/gitweb.conf
SetEnv GITWEB_PROJECTROOT /nfs/git_public
RewriteEngine on
# PATH_INFO usage for pretty URLs
# make sure to enable it in gitweb.conf
#
# $feature{'pathinfo'}{'default'} = [1];
#
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.* /git/gitweb.cgi/$0 [L,PT]
</Directory>
Our gitweb.conf
for gitweb:
# Set the path to git projects. This is an absolute
# filesystem path which will be prepended to the project
# path.
our $projectroot = $ENV{'GITWEB_PROJECTROOT'} || "/nfs/git_public"
# Set the list of git base URLs used for URL to where
# fetch project from, i.e. # the full URL is
# "$git_base_url/$project". By default this is empty
our @git_base_url_list = qw(https://hostname/git);
our $site_name = "Git Repository";
$my_uri = "/git";
$home_link = "/git";
$home_link_str = "https://hostname/git";
# logo needs to be 72x27
$logo = "https://hostname/git/static/git-logo.png";
$logo_url = "https://hostname/";
$favicon = "https://hostname/git/static/git-favicon.png";
$stylesheet = "https://hostname/git/static/gitweb.css";
$per_request_config = 0;
$feature{'pathinfo'}{'default'} = [1];
$projects_list_description_width = 140; # like twitter
$omit_owner = 1;
Now you should be able to access gitweb via https://hostname/git
and
repositories at https://hostname/git/repo.git
.
git-http-backend
(Most of this section is from git-http-backend(1).)
While we can access gitweb via https://hostname/git/repo.git
, some
more work is needed to be able to clone the repository via git over HTTP
(e.g., git clone https://hostname/git/repo.git
).
We’ll send requests that come from git to git-http-backend
; the
rest will get routed to gitweb.
(We don’t handle URLs with git://
, but that shouldn’t be too difficult.)
In git.conf
, add the following:
ScriptAliasMatch \
"(?x)^/git/(.*/(HEAD | \
info/refs | \
objects/(info/[^/]+ | \
[0-9a-f]{2}/[0-9a-f]{38} | \
pack/pack-[0-9a-f]{40}\.(pack|idx)) | \
git-(upload|receive)-pack))$" \
/usr/libexec/git-core/git-http-backend/$1
<Directory "/usr/libexec/git-core/">
SSLRequireSSL
SetEnv GIT_PROJECT_ROOT /nfs/git_public
SetEnv GIT_HTTP_EXPORT_ALL
Options +ExecCGI
Require all granted
</Directory>
You should now be able to clone repositories via git using the same gitweb URLs.
per-user repositories
gitweb
The section titled “Webserver configuration with multiple projects'
root” on the
gitweb(1) manual page
details how you could use mod_rewrite in Apache to serve gitweb
in home directories or different locations.
I was never able to get this to work properly on the Apache side,
but found an older guide here
that adds some Perl code to gitweb.conf
to serve user repositories.
I’ve modified the configuration slightly to additionally check if the public_git folder exists for a user.
if ($ENV{"PATH_INFO"} =~ m#^/~([^/]+)#) {
my $username = $1;
# check whether the user really exists
my @pwent = getpwnam($username);
# check if user has a public_git folder, otherwise fall back
if (@pwent && -d $pwent[7] . "/public_git") {
@git_base_url_list = "https://hostname/git/~" . $username;
# we only serve $HOME/public_git/
$projectroot = $pwent[7] . "/public_git";
# Fix up git's idea of the urls :/
$my_url .= "/~$username";
$my_uri .= "/~$username";
$home_link .= "/~$username";
$path_info =~ s#^/~[^/]+##;
# fix up some strings
$home_link_str .= "/~$username";
$site_name = "$username" . "'s repository";
}
}
A further improvement (TODO) would be to store symlinks in a directory
like /var/www/git/user/$user
which points to /home/$user/public_git
and set the $projectroot
to that symlink.
git-http-backend
To get per-user repositories via git, we set an environment variable
GIT_PROJECT_ROOT
based on the requested username and pass that to
git-http-backend
.
In git.conf
add the following to the beginning of the file:
(2014-04-01: Fixed a bug where the second group in the regex for
Request_URI should be (.*)
not (/.*/)
.)
SetEnvIf Request_URI "(?x)^/git/\~([^/]*)(.*)$" \
GIT_PROJECT_ROOT=/home/$1/public_git
# this takes care of user directories
ScriptAliasMatch \
"(?x)^/git/\~([^/]*)(/.*/(HEAD | \
info/refs | \
objects/(info/[^/]+ | \
[0-9a-f]{2}/[0-9a-f]{38} | \
pack/pack-[0-9a-f]{40}\.(pack|idx)) | \
git-(upload|receive)-pack))$" \
/usr/libexec/git-core/git-http-backend/$2
To prevent overwriting of the environment variable, we’ll set
GIT_PROJECT_ROOT
to a default value if there’s no value:
<Directory "/usr/libexec/git-core/">
SSLRequireSSL
# <If></If> requires apache 2.4
<If "-z %{ENV:GIT_PROJECT_ROOT}">
SetEnv GIT_PROJECT_ROOT /home/ant/ANT_GIT_PUBLIC
</If>
SetEnv GIT_HTTP_EXPORT_ALL
Options +ExecCGI
Require all granted
</Directory>
other notes
I have not tested the possible security implications for the above configurations.