Julius Plenz – Blog

cgit and setuid

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

  1. searches for git's user ID
  2. setuid()s to that ID (real an effective)
  3. executes the actual CGit CGI

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).

posted 2011-09-25 tagged git, cgit and lighttpd

Upgrading to Gitolite and CGit

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.

posted 2011-01-31 tagged git, cgit, gitolite and lighttpd