Authentifizierter Mercurial Webdienst

Um die Arbeit der Systemgruppe zu erleichtern und um vor allem mal weg von CVS zu kommen stellt sich die Frage ob es nicht eine sinnvolle, sichere und universell zugreifbare Möglichkeit gibt, ein Verteiltes Versionsverwaltungssystem zur Verfügung zu stellen.
Da viele Kollegen und ich bereits Mercurial (hg) einsetzen, hab ich mich in diese Richtung auf die Suche gemacht und bin fündig geworden.
Die Anforderungen an so einen Dienst sind:

  • leicht zugreifbar, egal welche Client-Plattform (wenn möglich nicht unbedingt über ssh)
  • mehrere Repositories/Projekte unter einem Hut
  • Authentifizierung beim Zugang
  • Verschlüsselung, da Passworte über’s Netz geschickt werden
  • Einzelprojekte mit individuellen Zugriffsrechten

Beim letzten Punkt war ich mir nicht sicher, ob der sich so einfach lösen lässt. Ich habe aber eine Möglichkeit gefunden. Doch der Reihe nach.

Voraussetzung ist, dass auf dem Server Mercurial (Python) installiert ist (eine aktuelle Version für Solaris zu paketieren ist kein Ding). Zweitens braucht man einen Apache (oder einen beliebigen anderen Webserver der SSL und Authentifizierung, in unserem Fall über LDAP) kann. Im Wesentlichen war’s das dann auch schon.

Kern des Dienstes ist ein Skript, welches mit Mercurial mitgeliefert wird (liegt im obersten Verzeichnis der hg-sourcen): hgwebdir.cgi. Es gibt noch ein zweites (hgweb.cgi), welches aber nur für ein einzelnes Repository gedacht ist. hgwebdir.cgi hingegen kann mehrere hg-repos verwalten.
Man sucht sich also ein Verzeichnis, wo hgwebdir.cgi samt config liegen dürfen und konfiguriert den Apache entsprechend, dass er dieses Verzeichnis auch als CGI-dir zulässt. Etwa mit folgendem Eintrag in der apache-config:

<Directory "/proj/hgweb/"\>
    DirectoryIndex hgwebdir.cgi
    AddHandler cgi-script .cgi
    Options ExecCGI
    Order allow,deny
    Allow from all
</Directory>
Alias /hg/ "/proj/hgweb"
ScriptAliasMatch ^/hg(.*) /proj/hgweb/hgwebdir.cgi$1

Jetzt sollte man noch eine .htaccess Datei ins /proj/hgweb Verzeichnis hinlegen. Damit wird die Benutzerauthentifizierung gemacht. Je nachdem wie sie erfolgen soll, können die Benutzer aus einer Datei, aus LDAP usw. kommen – je nach Bedürfnis und ganz so wie Apache das halt kann und macht.
Damit wäre der Apache Teil abgeschlossen – die Konfiguration, dass der Apache SSL spricht, ist trivial und unabhängig von Mercurial.
Jetzt sucht man sich noch ein passendes Verzeichnis wo die hg repos liegen werden. Es muss nicht alles in einem Verzeichnis sein, ist aber der Übersichtlichkeit wegen sinnvoll. Hat man einen geeigneten Platz gefunden, teilt man dies dem hgwebdir.cgi mit, indem man die config hgweb.config anpasst. Dies kann in etwa so aussehen:

[paths]
FAU-Solaris-pkg = /proj.stand/hg/FAU-Solaris-pkg
Mercurial Book = /proj.stand/hg/book
Solaris Autopatch = /proj.stand/hg/autopatch

Im obigen Beispiel gibt es drei Repositories, die alle im Verzeichnis /proj.stand/hg liegen. Der erste Teil des Eintrags ist der Name wie er in der Weboberfläche erscheint, der zweite der Pfad.
Jetzt wird’s ein bisschen kniffliger: man kann in der config Datei globale Einstellungen machen, die erstmal für alle Repositories gültig sind. So z.B. ist defaultmäßig ein hg push

nur erlaubt, wenn die Verbindung verschlüsselt ist. Auch ist ein bestimmtes Design der Oberfläche eingestellt, welches man ändern kann. All diese Änderungen kann man aber nochmal in und für die einzelnen Repositories machen (und damit die systemweite config überschreiben sobald man ein Repository aufruft). Aber dazu später mehr. Hier ein Beispiel für Einträge in hgweb.config:

[web]
push_ssl = false
style = gitweb
allow_push = frodo, sam
deny_push = saruman

Eingeleitet wird die web-config mit [web]. Mit push_ssl = false. Damit wird ein push (Upload) auch über nicht verschlüsselte SSL Verbidnungen zugelassen. Dies ist natürlich nicht sinnvoll und dient nur als Beispiel. Die Zeile mit style gibt an, wie die Mercurial Weboberfläche aussieht. Es gibt eine Handvoll mitgelieferter Styles – optisch gefällt mir persönlich „monoblue“ ganz gut, der im Beispiel verwendete „gitweb“ Style lehnt sich an der Weboberfläche von „git“ an.
allow_push und deny_push geben an, wer Änderungen pushen (hochladen) darf und wer nicht. Eigentlich ist die zweite Zeile obsolet – im laut allow_push dürfen nur die beiden Benutzer frodo uns sam pushen. Eine Möglichkeit wäre, allow_push = * zu setzen und anschliessend die obige deny_push Zeile zu verwenden. Damit darf jeder ausser der Benutzer „saruman“ Änderungen ins Repositroy pushen.
An dieser Stelle ein Hinweis: das Webscript verwendet die Login-Namen, mit den der Benutzer sich an den Apache angemeldet hat. Das heisst, dass die Einträge wer was darf natürlich nur dann funktionieren, wenn auch eine Authentifizierung vor das Mercurial-Web-Script „geschaltet“ ist.

Ich hatte schon erwähnt, dass jedes einzelne Repository für sich nochmal unabhängig von den anderen angepasst werden kann. Dies ist besonders sinnvoll, wenn nicht jeder Benutzer auf jedes repository zugreifen darf. Dazu editiert man die Datei

/PFAD/ZUM/REPO/.hg/hgrc

Die Syntax ist exakt dieselbe wie die von hgweb.config – oder besser gesagt: die Syntax von hgweb.config ist die von hgrc. Eine Liste aller möglichen Optionen verlinke ich am Ende.
Aufpassen sollte man hier, dass manche Zugriffsoptionen (etwa allow_pull (benutzt etwa beim clonen eines Repositories) nur für einen hg-Zugriff gelten, nicht aber für den Zugriff über die Weboberfläche. Möchte man den Lesezgriff generell (auch über Web) verbieten, verwendet man die Direktive allow_read bzw. deny_read. Damit ist das Projekt zwar über die Weboberfläche sichtbar aber nicht für jedermann zugreifbar.
Möglich ist auch noch eine filigrane Rechtevergabe via ACLs, welche in neueren Mercurial Versionen unterstützt werden. Damit kann man sagen, welcher Benutzer auf welche Dateien oder Verzeichnisse innerhalb des Repositories zugreifen oder schreiben (pushen) dürfen. Hier ein Beispiel aus der Mercurial Wiki Seite:

[extensions]
hgext.acl=

[hooks]
pretxnchangegroup.acl = python:hgext.acl.hook

[acl]
sources = serve        # check if source of incoming changes in this list
                       # ("serve" == ssh or http, "push", "pull", "bundle")

[acl.allow]
# if acl.allow not present, all users allowed by default
# empty acl.allow = no users allowed
docs/** = doc_writer
.hgtags = release_engineer

[acl.deny]
# if acl.deny not present, no users denied by default
# empty acl.deny = all users allowed
glob pattern = user4, user5
** = user6

Damit sollte nun die Basisinstallation eines Mercurial Webservices möglich sein, der nicht nur eine praktische Weboberfläche und Sicht auf Dateien, Änderungen und Logs gibt, sondern auch Zugriff auf Repositories über (potenzielle) Firewall Grenzen hinweg: Port 80 (http/WWW) ist in der Regel nicht durch eine Firewall blockiert. Der CLient kann nun mit einem

hg clone https://hg.site.org/hg/MEINPROJEKT

das Repository lokal klonen, damit arbeiten und auf denselben weg wieder pushen und somit seine Arbeit den Anderen zugänglich machen. hg übernimmt auch noch praktischerweise die Authentifizierung: entweder man gibt die Credentials in der URL mit an (https://benutzer:passwort@hg.site.org/…) oder sie werden beim pullen oder pushen abgefragt (was empfehlenswerter ist)

Empfehlenswert ist das Buch „Mercurial: the Definitive Guide“, welches man kostenlos hier findet. Aber auch das Wiki auf der Mercurial Homepage ist eine Anlaufstelle bei Problemen. Hier noch ein paar Links: