I'm still in the process of setting up my new server. Today, I migrated the Git repositories. I wanted a more secure setup, that is I don't want my web server to be able to read the repositories. (It spawns CGit, which has to read them somehow.)
So I created all repositories below /var/git/repositories
, such that
they are only readable by the system user "git".
However, that presents a problem to the CGit CGI: It cannot access the
repositories any more. My first approach was: Just chown git.git
/usr/lib/cgi-bin/cgit.cgi
and set the setuid bit on it.
But, alas, that will only make the CGit binary run with effective user ID "git". What's needed to actually access the files is a real user ID "git".
The only way to set the real user ID is to call setuid()
with
effective root privileges. So I created a wrapper that
setuid()
s to that ID (real an effective)That's what I came up with:
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <string.h>
int main(int argc, char *argv[])
{
struct passwd *p;
uid_t git_uid = 0;
while((p = getpwent()) != NULL) {
if(strcmp(p->pw_name, "git"))
continue;
/* got user "git" */
git_uid = p->pw_uid;
}
endpwent();
if(!git_uid)
return 1;
setuid(git_uid);
execv("/usr/lib/cgi-bin/cgit.cgi", argv);
return 0;
}
Provide it with a Makefile... (beware, use real tabs!)
default:
gcc -Wall -o cgit-as-git.cgi cgit-as-git.c
install:
install -o root -g root -m 4755 cgit-as-git.cgi /usr/lib/cgi-bin
... and change the Lighttpd configuration accordingly:
$HTTP["host"] == "git.plenz.com" {
setenv.add-environment += ( "CGIT_CONFIG" => "/etc/cgit/plenz.com.conf" )
alias.url = (
"/cgit.css" => "/usr/share/cgit/cgit.css",
"/cgit.png" => "/usr/share/cgit/cgit.png",
"/cgit.cgi" => "/usr/lib/cgi-bin/cgit-as-git.cgi",
"/" => "/usr/lib/cgi-bin/cgit-as-git.cgi",
)
cgi.assign = ( ".cgi" => "" )
url.rewrite-once = (
"^/cgit\.(css|png)" => "$0",
"^/.+" => "/cgit.cgi$0"
)
}
Done! Lighttpd can now call CGit as if it was a "usual" binary, but it will get wrapped and get the real and effective user ID of the system's user "git".
N.B.: If you want to use the wrapper as well, you might want to change
the (hard coded) user name "git" and the binary (see execv
call).
I'm in the process of setting up a new server. I struggled for some
minutes now with the follwing strange behaviour: Lighttpd is very
picky (and not at all verbose) about when you use
alias.url
.
As an actual example, look at these two blocks:
$HTTP["host"] == "HOST" {
alias.url += ("/munin" => "/var/www/munin")
}
$SERVER["socket"] == ":443" {
$HTTP["host"] == "HOST" {
alias.url += ("/wiki" => "/usr/share/dokuwiki",)
}
}
Now I, the innocent user, would expect the following to happen:
/munin
/wiki
But, alas, nope. It's quite different. The wiki is available alright if called before the munin part, and vice versa. The other one will be a 404, though. To make things work I actually have to double the alias part for munin – once outside the part that matches for SSL and once inside.
Oh, and don't confuse ==
and =~
– even
if both match, the exact match wins. (Meaning you cannot put the line
in a condition like =~ ":(443|80)"
.) So there's no real
cascading or so. Bad design in my opinion, but it
seems to be
intentional. WTF?
Correct solution:
$HTTP["host"] == "HOST" {
alias.url += ("/munin" => "/var/www/munin")
}
$SERVER["socket"] == ":443" {
$HTTP["host"] == "HOST" {
alias.url += ("/munin" => "/var/www/munin")
}
}
$SERVER["socket"] == ":443" {
$HTTP["host"] == "HOST" {
alias.url += ("/wiki" => "/usr/share/dokuwiki",)
}
}
Holy crap. Explain that to someone unfamiliar with how difficult it is to implement data structures in C. – They'd just call you a moron for not implementing a more intuitive aproach. – Wait, I do, too!
I finally found some time to update the git version used on my server. It's running a 1.7.1 now, which is just slightly better than the previous 1.5 version. ;-)
Debian package maintainers bother me again, though. The simply erase
the git
user and put a gitdaemon
user in it's place, without
asking for confirmation. Will probably happen with the next upgrade,
too...
Now, having a recent git version enables me to set up gitolite instead of the old gitosis. Setup is easy, straight-forward and described in many places.
I used this script to convert my existing gitosis.conf
to match
gitolite's syntax; I noticed a major hiccup though: setting a
description automatically enables gitweb access. I had to read
the source to find this out, because I couldn't remember it from the
docs and it didn't once cross my mind that you could actually want
the behaviour to be like that. – As in, you could just describe
a project without wanting to make it public – no?
However, with gitolite, I can finally use CGit too (because only
gitolie produces a repositories.list
which is in the format that
CGit understands, ie. without description appended to the lines).
So now the repository page is all new and shiny. See, for example, this side-by-side diff. Nice, and faster than Gitweb.
Integrating CGit with Lighttpd was another two-hour fiddle-try-fail-repeat session. I finally came up with this snippet:
$HTTP["host"] =~ "^git\.(plenz\.com|feh\.name)(:\d+)?$" {
alias.url = (
"/cgit.css" => "/usr/share/cgit/cgit.css",
"/cgit.png" => "/usr/share/cgit/cgit.png",
"/cgit.cgi" => "/usr/lib/cgi-bin/cgit.cgi",
"/" => "/usr/lib/cgi-bin/cgit.cgi",
)
cgi.assign = ( ".cgi" => "" )
url.rewrite-once = (
"^/cgit\.(css|png)" => "$0",
"^/.+" => "/cgit.cgi$0"
)
}
It's important here that you noop-rewrite the CSS and PNG file to
make Lighty expand the alias. Also, rewriting to an absolute filepath
won't work; that's why the cgit.cgi
aliases have to appear twice.