Individuelle, geschützte und dynamisch erstellte SVN-Repositories

facesDas Problem: Es sollen individuelle SVN Verzeichnisse erstellt werden. Ein Bereich also, in dem eine Person beliebig viele Repositories erstellen kann, die nur von ihr selbst angesehen und bearbeitet werden können. Mann könnte jetzt meinen – vorallem mit viel jugendlichem Leichtsinn – das sei ein einfaches, allgemein bekanntes Problem. Pustekuchen! Das muss ein ganz fremder wohl ganz abstruser Wunsch sein.

Verschiedene Dinge schränken die Chance ein, eine einigermassen vernünftige und allgemeine Lösung zu finden 🙁 .Doch der Reihe nach. Was ist gegeben:

  • Ein wunderbarer Server, mit SSL und allem.
  • Das Einrichten eines neuen Users sollte ungefähr 0,01 Sekunden menschlicher Arbeitskraft bedürfen.
  • Die Benutzer sind in einem Active Directory gespeichert.
  • Apache2, PHP, und alles was man will ist installiert.

Einschränkende Dinge

Die grösste Peinlichkeit ist die Direktive SVNParentPath (von mod_dav_svn). An sich sollte die Direktive ein Basisverzeichnis für die SVN-Repositories angeben aber:

  • Der Parameter (das Verzeichnis) ist absolut undynamisch. Er nimmt keine Umgebungsvariable, Regexp oder sonst was, sondern ganz stur nur einen festen Pfad.
  • Kann nicht in einer .htaccess Datei gesetzt werden

Gna! Das ist echt 2002! So wird das nichts mit dynamisch erstellten SVN-Verzeichnissen. Aus reinem Masochismus hane ich verschiedene Lösungen wie mod_rewrite, symbolischen Links, LocationMatch und alles was sonst noch so Zeit kostet ausprobiert. Keine Chance…
Erschwerend hinzu kommt, dass sich SVN überhaupt nicht mit der Alias Direktive anfreunden kann und konsistent stur auf eine jungfräuliche Location beharrt: Der Versuch wird mit einem Repository moved permanently, please relocate belohnt.

Die Krönung: mod_authz_path

mod_authz_path klingt vielversprechend. An Hand des Pfades sollte der User und das Realm festgelegt werden können. Jaaa, aber nicht als Location, sondern nur als Pfad (<Directory> im Apache), wie man mit dem Debuggen feststellen kann:

AuthNamePathMatch (.+) "Persoenliches SVN $1"
AuthzUserPathMatch (.+) $1

Mit dem Pfad findet sich aber – wie schon weiter oben geflucht – das SVN nicht zurecht.

Ganz nebenbei hat mich dieses Modul etwa viertausend Stunden gekostet. Wenn es geladen wird, funktioniert authnz_ldap nicht mehr richtig. Nicht überhaupt nicht, sondern einfach das require Statement spielt verrückt und verhält sich unberechenbar. Nach langer Zeit habe ich dann das auch gemerkt :irre: . Aber erst nachdem ich unter viel Wehklagen in einer Panik die anderen Server getestet habe, ob die Authentifizierung dort auch nur scheinbar funktioniert.

Die Lösung

Nach ein paar Jahren die Lösung: Gutes, altes PHP.

Auf dem SSL-Port des Servers ist eine Website, auf der sich die User mal als erstes über Apache am Active Directory authentifizieren müssen. Dadurch habe ich Zugriff auf den Benutzername mittels $_SERVER['AUTHENTICATE_SAMACCOUNTNAME']. Loggt sich jemand ein den wir noch nicht kennen, dann:

  1. Wird der svn-Grundpfad erstellt, wenn er noch nicht existiert.
  2. Wird eine Apache-Konfiguration für diesen neuen Grundpfad erstellt, die das ParentPath und den entsprechenden User beinhaltet.
  3. Wird Apache neu gestartet → und der Benutzer währenddessen zum Warten gebracht. Das ist leider notwendig zum Einlesen der Konfigurationserweiterung.

Die neue Konfigurationsdatei wird „lokal“ erstellt und in der Grundkonfiguration /etc/apache2/sites-available/isvn.myhost.ch eingebunden:

        Include /data/www/isvn.myhost.ch/config/svn.conf

PHP Code für die neuen Benutzer:

  $svn_user=strtolower($_SERVER['AUTHENTICATE_SAMACCOUNTNAME']);
  if(!$svn_user) { echo "Hacker, you will be disintegrated!"; exit(1); }
  $svn_root=SVNROOT."/".$svn_user;
  if(!is_dir($svn_root)) { // NEW USER
        // Make svn Parent Path
        mkdir($svn_root);
        // Write Config
        $fh = fopen(SVNCONF, 'a') or die("can't open file");
        $svnDirective =<<<EOD

<Location /svn/lp/$svn_user>
        DAV svn
        SVNParentPath /data/svn/lp/$svn_user
        AuthName "Persoenliches SVN von $svn_user"
        AuthType Basic
        AuthBasicProvider ldap
        AuthLDAPBindDN ldapanonymous@myhost.local
        AuthLDAPBindPassword g3h31M3Sp+
        AuthLDAPUrl ldap://adc1.myhost.local/ou=Accounts,dc=myhost,dc=local?sAMAccountName?sub
        require ldap-user $svn_user
</Location>

EOD
;
        fwrite($fh, $svnDirective);
        fclose($fh);

        // Make th User wait
        echo "<html><head><title>SVN wird eingerichtet</title>\n";
        echo '<meta http-equiv="refresh" content="5; URL=https://isvn.myhost.ch">'."\n";
        echo "</head><body>".implode(" ", explode(".",$_SERVER['AUTHENTICATE_SAMACCOUNTNAME'])).": Ihr SVN Grundverzeichnis wird erstellt. Bitte warten Sie.</body></html>";

        // Restart Apache
        exec('echo "/usr/bin/sudo /etc/init.d/apache2 restart" | /usr/bin/at now');
        exit();
  }

Damit der User www-data den Apache restarten darf, muss ihm das per /etc/sudoers erlaubt werden:

www-data ALL=NOPASSWD: /etc/init.d/apache2

Dass die Berechtigungen auch für alle benötigten Zusatzdateien reichen, machen wir den Trick mit dem at.

Somit haben wir also eine hochindividuelle Konfigurationsdatei für alle User und Ihr SVN Grundverzeichnis. Die PHP-Seite ermöglicht es, SVN-Repositories anzulegen und zeigt auch gerade den annektierten Platz an:

Die SVN-Steuerkonsole

Die SVN-Steuerkonsole

Die Quellen

Hier sind die PHP-Quellen. Für weitere Details stehe ich gerne zur Verfügung.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.