www.h-ein.de

Gesicherter CVS-Server in chroot-Umgebung

Achtung, öffnet in einem neuen Fenster. Drucken

SchlossJedem Admin wird flau im Magen, wenn es darum geht einen öffentlich zugänglichen CVS-Server aufzusetzen. Das von CVS genutzte pserver-Protokoll hat gerade in puncto Sicherheit einige Mängel. Allein der Umstand, dass die Datenübertragung zwischen Client und Server ohne Verschlüsselung stattfindet und Passwörter und Dateien aus dem Repository im Klartext durch's Netz wandern, erzeugt wenig Vertrauen. Zudem hatte GNU CVS in der Vergangenheit häufig mit Sicherheitsproblemen zu kämpfen.

Abhilfe schafft hier der Einsatz von SSH als Übertragungsprotokoll. Wenn man den CVS-Server dann auch noch in einer chroot-Umgebung betreibt, sollte man als Admin wieder ruhig schlafen können.

Wie man einen so gesicherten CVS-Server unter OpenBSD konfiguriert möchte ich in dem folgenden Artikel beschreiben.

0. Vorbemerkung

Die Partition, auf der das “Gefängnis” für den CVS-Server und das Repository später untergebracht werden soll, sollte nicht mit den Optionen “nodev“ und “noexec“ gemountet sein. Wird die chroot-Umgebung wie im nachfolgenden Text auf der /home-Partition eingerichtet, dann muss vorher in der Datei /etc/fstab geprüft werden, ob die vorher genannten Optionen gesetzt sind und diese dann ggf. entfernt werden.

So könnte ein für den geplanten Einsatzzweck tauglicher Eintrag für die /home-Partition aussehen:

# grep »/home« /etc/fstab
/dev/sd0h /home ffs rw,nosuid,softdep 1 2

1. chroot-Umgebung erstellen

Damit CVS und SSH in der chroot-Umgebung laufen, muss ein bißchen Vorarbeit geleistet werden. Unter anderem müsen ein paar benötigte Programme und Bibliotheken kopiert werden. Zuerst erstellen wir ein Verzeichnis, in dem die chroot-Umgebung eingerichtet werden soll. In unserem Fall soll die chroot-Umgebung im Verzeichnis /home/cvs_chroot ihre neue Heimat finden. Als nächstes erstellen wir eine grundlegende Verzeichnisstruktur:

# mkdir /home/cvs_chroot
# cd /home/cvs_chroot/
# mkdir -p bin dev etc lib libexec/auth sbin tmp \
var/empty var/run var/chroot/sshd
# ln -s . usr
# chmod 1777 tmp

Es wird außerdem noch ein Link usr der auf die Wurzel der Chroot-Umgebung zeigt. Das vereinfacht die Verzeichnisstruktur. Zudem ändern wir noch Berechtigungen des tmp-Verzeichnisses auf 1777.

Als nächstes kopieren wir alle benötigten Dateien und Bibliotheken:

# cd /home/cvs_chroot
# cp /bin/ksh /bin/ls /bin/rm /bin/mkdir /bin/cp \
/usr/bin/cvs /usr/bin/passwd /usr/bin/login bin \
/bin/mv /bin/echo
# cp /sbin/nologin /usr/sbin/pwd_mkdb \
/usr/sbin/sshd sbin
# cp /etc/master.passwd /etc/group etc
# cp /usr/libexec/ld.so libexec
# cp -rf /etc/ssh etc
# cp /etc/login.conf etc
# cp /usr/libexec/auth/login_passwd libexec/auth
# cp `ldd bin/passwd sbin/sshd \
libexec/auth/login_passwd | \
grep -v -e 'libexec' -e 'bin' -e 'sbin' \
-e 'Start' | awk '{print $7}' | sort | \
uniq` lib

Zum Schluss erstellen wir noch fehlende Gerätedateien im dev-Verzeichnis:

# cd /home/cvs_chroot/dev
# /dev/MAKEDEV std pty0 random

2. Konfiguration der Benutzer

An diesem Punkt editieren wir die Nutzer- und Gruppendateien und werfen alle nicht gebrauchten Einträge raus und fügen gewünschte Einträge ein:

# cd /home/cvs_chroot
# vi etc/group

wheel:*:0:root
sshd:*:27:
_shadow:*:65:
nogroup:*:32766:
nobody:*:32767:
cvs:*:500:
# vi etc/master.passwd

root:*:0:0:daemon:0:0:root:/root:/bin/ksh
sshd:*:27:27::0:0:sshd privsep:/var/empty:/sbin/nologin
nobody:*:32767:32767::0:0:Unprivileged user:/nonexistent:/sbin/nologin
cvs:*:500:500::0:0:cvs:/cvs:/sbin/nologin
ahein:*:1000:500::0:0:Andreas Hein:/home/ahein:/bin/ksh

# mkdir /home/ahein
# chmod 555 /home/ahein

Wir haben also in unserer User-Datenbank einen Nutzer ahein für den wir auch nach dem Editieren der Datei master.passwd ein Homeverzeichnis anlegen. Dieses verzeichnis ist wichtig, wenn der Nutzer später seinen SSH-PublicKey hinterlegen möchte. Ansonsten könnte man auch /var/empty als Homeverzeichnis angegeben werden.

Jetzt müssen nur noch die fertigen Userdatenbanken generiert werden:

# chroot /home/cvs_chroot
# passwd ahein
Changing local password for ahein.
New password:
Retype new password:
# exit

Wenn keine Fehler auftreten scheint soweit alles zu funktionieren.

3. Konfiguration des Servers

Nachdem die chroot-Umgebung nun fertig eingerichtet ist können wir uns daran machen, den chroot-SSH-Server zu konfigurieren. Zuerst generieren wir dazu für den CVS-Server eigene SSH-Schlüsselpaare. Beim Ausführen der folgenden Befehle wird gefragt, ob die vorhandenen Schlüssel überschrieben werden können. Diese Frage sollte mit “ja” beantwortet werden, da es sich hierbei um die (alten) Schlüssel handelt, die wir in Schritt 1 mit in die chroot-Umgebung kopiert haben.

# cd /home/cvs_chroot/etc/ssh
# /usr/bin/ssh-keygen -t rsa1 -b 1024 -f ssh_host_key -N ''
# /usr/bin/ssh-keygen -t dsa -f ssh_host_dsa_key -N ''
# /usr/bin/ssh-keygen -t rsa -f ssh_host_rsa_key -N ''

Als nächstes müssen wir dem chroot-sshd noch beibringen, einen anderen Port als den Standard-Port 22 zu nutzen. Da auf dem Host-System wahrscheinlich schon ein SSH-Server auf diesem Port läuft, käme es ansonsten zu einer Kollision. Dazu bearbeiten wir die Datei /etc/ssh/sshd_config mit dem Editor unserer Wahl. Nachfolgend sind die Änderungen an der Datei sshd_config aufgelistet:

# cd /home/cvs_chroot/etc/ssh
# vi sshd_config

Port 2022

X11Forwarding no
AllowTcpForwarding no
ForceCommand cvs server

Unser Server wird also auf dem Port 2022 auf Verbindungen warten. Die letzten drei Einträge am Ende der Datei sind nicht unbedingt nötig, sollte aber auch ein anonymer read-only CVS-Zugriff ermöglicht werden, sollten die Einträge nicht fehlen, um Missbrauch des Servers zu verhindern. Die Einträge beschränken alle Nutzer auf die Nutzung von cvs (kein Shell-Aufruf mehr möglich), verbieten X11-Forwarding und das Tunneln von TCP-Verbindungen.

Das Starten des Servers ist relativ einfach:

# chroot /home/cvs_chroot /sbin/sshd

4. Client-Zugriff konfigurieren

Da unser CVS-Server nicht auf dem Standard-Port läuft, auf dem der ssh-Client ihn erwarten würde, müssen wir dem Client beibringen, beim Zugriff den neuen Port 2022 zu nutzen. Da man dem CVS-Kommando leider die Portnummer nicht mitgeben kann, muss der ssh-Client selber diese Aufgabe übernehmen. Dazu ist vom CVS-Nutzer die eigene SSH-Konfiguration anzupassen:

$ vi ~/.ssh/config

Host cvs.myserver.org
Port 2022

Dieser Lösungsansatz setzt allerdings vorraus, das man dem CVS-Server einen eigenen Namen im DNS geben kann. Gibt man hier z.B. die IP des Servers oder den “normalen” Namen des Servers an, dann wird ein Anmelden am System über den “richtigen” SSH-Server etwas schwieriger, da ssh zuerst immer den Port 2022 zu kontaktieren versucht. Es reicht aber aus, einen Eintrag in der Datei /etc/hosts zu machen.

Den Zugriff auf den chroot-SSH testen wir nun, indem wir versuchen uns anzumelden:

$ ssh ahein@cvs.myserver.org

The authenticity of host '[cvs.myserver.org]:2022 ([::1]:2022)'
can't be established.
RSA key fingerprint is
b1:73:73:d7:ae:1f:cd:79:dd:fb:8b:e6:64:7b:30:bf.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[cvs.myserver.org:2022' (RSA) to the
list of known hosts.
ahein@cvs.myserver.org's password:
^C

$

Beim ersten Anmeldeversuch sollte ssh über den unbekannten RSA-Schlüssel informieren. Daran erkennen wir auch, dass das Generieren der Schlüssel in Schritt 3 funktioniert hat. Wenn wir nun das korrekte Passwort unseres CVS-Nutzers eingeben sollte gar nichts passieren. Der Cursor bleibt einfach stehen und es passiert nichts. Und genau so soll es auch sein. Bei der Konfiguration des Servers haben wir Shell-Access ausgeschlossen, weil nur cvs-Kommandos erlaubt sind. Mit Strg-C kann man zur Shell zurückkehren. Der chroot-SSH-Server funktioniert.

5. Initialisierung des Repositorys

Nachdem nun der Server läuft kann das Repository vom Client aus initialisiert werden. Dazu müssen nur die CVS-Umgebungsvariablen CVSROOT und CVS_RSH gesetzt werden:

$ export CVSROOT=:ext:ahein@cvs.myserver.org:/cvs
$ export CVS_RSH=ssh
$ cvs init
ahein@cvs.myserver.org's password:
$

Bemerkung: Die beiden Zeilen zum Setzen der Umgebungsvariablen können in der Datei ~/.profile des Nutzers gespeichert werden. So sind sie nach dem Login automatisch gesetzt.

Nun, nachdem das Repository erfolgreich angelegt wurde sollte man noch die Berechtigungen überprüfen. Das machen wir auf dem Server mit folgenden Kommandos:

  1. cd /home/cvs_chroot/cvs
  2. find . -type d -exec chown 500:500 {} \;
  3. find . -type d -exec chmod 775 {} \;
  4. find . -type f -exec chown 500:500 {} \;
  5. find . -type f -exec chmod 664 {} \;

Unser gesicherter CVS-Server ist jetzt fertig und einsatzbereit.

6. Zum Schluss

Damit der Server auch automatisch beim Hochfahren des OpenBSD-Systems gestartet wird, sollte man die folgende Zeile in der Datei /etc/rc.local unterbringen:

echo ' cvs';
/usr/sbin/chroot /home/cvs_chroot /sbin/sshd

Hinweis: Unter Linux sollte man sich ein kleines Init-Script schreiben, das diese Aufgabe übernimmt.

Will man den CVS-Server beenden, so findet man die ProcessID in der Datei /home/cvs_chroot/var/run/sshd.pid. Das führt uns zu folgendem Aufruf von kill(1):

# kill `cat /home/cvs_chroot/var/run/sshd.pid`

Das Anlegen und Löschen von CVS-Nutzern ist durch das chroot etwas komplizierter. Es muss die Datei /home/cvs_chroot/etc/master.passwd bearbeitet werden und anschließend wie in Punkt 2 mit dem Befehl pwd_mkdb neue Userdatenbanken erzeugt werden. Wenn man häufig neue User hinzufügt oder löscht sollte man sich dafür ein kleines Shell-Script schreiben. Eine andere Möglichkeit wäre es, die Tools useradd und userdel mit in die chroot-umgebung zu kopieren — das ist aber meiner Meinung nach keine so gute Idee, da so ein chroot-Gefängnis nur das nötigste enthalten sollte.

Um die ständigen Passwort-Abfragen beim Nutzen von CVS zu vermeiden, können Nutzer ihren SSH-Publickey in ihrem Homeverzeichnis im chroot ablegen. Das kann root z.B. so erledigen:

# mkdir -p /home/cvs_chroot/home/ahein/.ssh
# cp id_rsa.pub /home/cvs_chroot/home/ahein/.ssh/authorized_keys

Viel Spass mit dem neuen, gesicherten CVS-Server den man so auch der Öffentlichkeit zugänglich machen kann, ohne schlaflose Nächte zu haben.

»Teile Dein Wissen. Das ist ein Weg, Unsterblichkeit zu erlangen.« — Dalai Lama