KnastHorst
Auf meinem gute-Freunde-Shell-Server, auf dem auch diese edle Seite liegt, hatte ich bisher ein FreeBSD4.(5-11) installiert. In diesem wurden 14 sogenannte Jails gefahren. Das ist der FreeBSD-Ansatz, chroot soweit zu treiben, damit in virtuellen FreeBSD-Systemen root-Rechte vergeben zu koennen, ohne damit die Integritaet des Hostsystems zu beeintraechtigen.
Einige jails werden als shared service von $kumpel und mir betrieben, so ein mail-, ein www- und ein nameserver, andere in die Hand von Freunden gedrueckt. Da der Server wegen .. sagen wir, Rechenschwaechen .. des derzeitigen Hosters dort nicht mehr stehen bleiben kann und somit ein Umzug auf andere Hardware notwendig wird, werde ich die Gelegenheit nutzen, auf ein FreeBSD5 oder 6 zu wechseln.
Das bringt mehrere Vorteile mit sich: a) sind nuetzliche Tools zur Jailverwaltung, naemlich jls
und jexec und 'kill -j' hinzugekommen, b) hat es das script /etc/rc.d/jail
und
macht jails laufen lassen zum Spass und c) sind da viele nette Features im Kernel, wie z.B. KSE,
die das OS snappier[tm] machen sollten.
Bisher musste ich alle jails muehsam von einem selbstgehackten Scripteset kontrollieren lassen.
Um nicht fuer jedes jail ein komplettes Betriebssystem auf der Platte herumliegen zu haben
(kostet viel Platz), wird die Welt aufgeteilt in ein zentral gewartetes basejail (/bin
/sbin /usr/bin /usr/include /usr/lib /usr/libexec /usr/sbin /usr/src /usr/share /usr/ports
/usr/src
) und die zweite, vom User anpassbare Haelfte, das "newjail" (kostet wenig Platz,
um die 8MB). Ersteres wird nun readonly in jedes Jail (nach /usr/jails/*/basejail
)
gemounted. Softlinks lassen dann z.B. /usr/bin
nach /basejail/usr/bin
zeigen. (man jail
zeigt die Schritte, die man braucht, sich ein basejail zu
basteln, ein lokales cvs-repository hilft). Soweit, so gut.
Nun gab es dieses kleine Problem: fuer das simple loopback-Mounten eines Verzeichnisses hat sich FreeBSD mount_nullfs ausgedacht. Doch ein kleiner Blick in die man-page macht einem den Mut, den man braucht, seinen Mailserver darauf aufzubauen: "THIS FILE SYSTEM TYPE IS NOT YET FULLY SUPPORTED (READ: IT DOESN'T WORK) AND USING IT MAY, IN FACT, DESTROY DATA ON YOUR SYSTEM. USE AT YOUR OWN RISK. BEWARE OF DOG. SLIPPERY WHEN WET." Meine ersten Versuche vor ein paar Jahren ergaben genau dies: crashes und komische Effekte im Filesystem. Spaetere Experimente von Freunden erbrachten zwar keine Crashes mehr, dafuer aber ploetzliche hohe CPU-Load. Aus diesem Grund werkelt nun zur Zeit auf dem Server noch ein nfs-server, der fuer localhost das basejail exportiert und lauter mount_nfs, die es wieder mounten. (Dazu muss man erstmal portmap patchen) Mit so einem Setup kann man nicht prahlen gehen :(
Neulich entdeckte ich jedoch die
Todoliste fuer die 6.0er Release, in der angedeutet wird, dass "Nullfs (and perhaps
other filesystems) use an absurdly small hash size that causes significant performance
penalties." Der Source (/usr/src/sys/fs/nullfs/null_subr.c
) verriet mir
auch #define NNULLNODECACHE 16
. Also, wenn die zu kleinen Hashs deren einziges
Problem sind... Ich habe aus der 16 eine 65536 gemacht, neuen Kernel gebaut und habe nun das
basejail endlich per nullfs gemounted. (Zur Zeit laufen 12 Jails auf einer Testinstallation, die
dann auf den neuen Server uebernommen wird.)
Die Features des jail-scripts aus der /etc/rc.d
sind zwar grossartig, aber echt
unglaublich wirklich voll total mies dokumentiert. Die jails, die das System starten soll,
traegt man space-separiert in der /etc/rc.conf
in jail_list="JAILNAME1
JAILNAME2..."
ein. Dann macht man noch jail_enable="YES"
an und beim Startup
werden alle jails hochgefahren. Die Parameter dafuer traegt man in Variablen wie zum Beispiel
jail_JAILNAME_ip="10.1.1.200"
ein (nicht vergessen, der Netzwerkkarte auch die
aliase fuer alle IPs zu geben). Punkte sind in den Variablen ungern gesehn, bei mir heisst das
dann immer erdgeist_org, also spaeter auch
jail_erdgeist_org_option_enable="YES"
.
Dann ist cool, dass jedes jail eine eigene fstab mitbekommt. Wer
jail_JAILNAME_mount_enable="YES"
anhat, kann beim jail Starten
/etc/fstab.JAILNAME
mounten lassen. Bei mir steht da naetuerlich
"/usr/jails/basejail /usr/jails/JAILNAME/basejail nullfs ro 0 0"
. Fertig ist der
Lack. Wer jail_JAILNAME_devfs_enable="YES"
(immer gern genommen mit
jail_JAILNAME_devfs_ruleset="devfsrules_jail"
, wegen der Sicherheit, wissenschon)
anhat, findet auch gleich ein /dev im jail gemounted vor, aehnlich verhaelt es sich mit
jail_JAILNAME_procfs_enable="YES"
und
jail_JAILNAME_fdescfs_enable="YES"
.
Das Verwalten der jails ist nun simpel, eigentlich haette das gleich vom rc.d-script mit
erledigt werden koennen: Man legt sich ein /etc/jails/
oder so an, in das man die
config-Bloecke fuer jeweils ein jail zusammenfasst, also z.B.
/etc/jails/erdgeist_org
und schreibt in seine /etc/rc.conf
jail_list=
ls /etc/jails/`, und included danach
. /etc/jails/*`.
Beim Erzeugen eines neuen jails kopiert man aus dem "newjail" mittels mkdir
/usr/jails/$JAILNAME && cd /usr/jails/newjail && find * | cpio -p -d -v
/usr/jails/JAILNAME
das Skelett. /etc/resolv.conf
,
/etc/rc.conf
, /etc/passwd
und
/home/admin/.ssh/authorized_keys
im newail gleich zu bevoelkern macht sich auch
immer gut, sonst vergisst man das. (Auch wichtig fuer den sshd: nicht vergessen, dass man im
Hostsystem alle Services nur auf die IP des Hostsystems binden lassen sollte, sonst kann man das
im Jail nicht mehr.) Danach erzeugt man (beispielsweise aus einer template-config) das File, was
nach /etc/jails/JAILNAME
soll. Ausserdem legt man die
/etc/fstab.JAILNAME
an. Done.
Da wir /usr/ports
mit dem basejail readonly gemounted haben (dadurch reicht es,
einmal jede Nacht ein cvsup
auf die ports im Hostsystem zu machen), muessen wir den
jails sagen, dass sie die ports nicht in /usr/ports/X/Y/work
sondern irgendwo
anders bauen sollen, wo man schreiben darf, die distfiles koennen natuerlich auch nicht nach
/usr/ports/distfiles
. Das macht man in der /etc/make.conf
. Bei mir
steht da WRKDIRPREFIX=/var/ports
und DISTDIR=/var/ports/distfiles
. Das
schreibt man am besten gleich ins newjail.
Diese Vorgehensweise steht eigentlich auch dem Hostsystem gut. Man kann naemlich spielen alle
work-directories und distfiles an einem zentralen Punkt loeschen: rm -rf
/var/ports/*
statt rekursivem make distclean
oder rm -rf
*/*/work/
in /usr/ports
. Eigentlich koennte man die distfiles zwischen den
jails noch in einem unionfs teilen, aber erstens liest sich die Doku zu mount_unionfs NOCH
entmutigender, als die von mount_nullfs und zweitens muesste fuer den Fall, dass korrupte
distfiles rumliegen, immer ein hostsystem-Admin putzen kommen.
Nun zum wirklich Betrieb der jails: Einzelne jails kann man mit sudo sh /etc/rc.d/jail
start JAILNAME
anstossen, die jail_list
wird genommen, wenn man keinen
jail-Namen angibt. Alle laufenden jails, inklusive ihrer jail-id kann man sich mit
jls
angucken. Nachtraeglich tasks an ein jail haengen geht mit sudo jexec
jail-id cmd
, wobei man da meist /bin/csh
nimmt. Mit sudo jexec jail-id
ps auxw
kann man sich dann die laufenden Programme im jail angucken. Beim Traversieren der
jail-Verzeichnisse (bei mir in /usr/jails) aus dem Hostsystem sollte man DRINGEND auf softlinks
aufpassen. Gerne verpeilt werden /usr/jails/JAILNAME/home -> /usr/home
, was einem
Aerger mit den Homeverzeichnissen im Hostsystem einbringen kann und natuerlich
/usr/jails/JAILNAME/usr/bin -> /basejail/usr/bin
. Jail anhalten geht mit sudo
sh /etc/rc.d/jail stop JAILNAME
.
Zu guter letzt noch ein paar Fallstricke: im jail geht ping nicht. Das liegt daran, dass man
keine raw sockets aufmachen darf, da man in diese natuerlich jede IP als Source IP eintragen
koennte und damit das Sicherheitskonzept der jails umginge. ping ist aber sehr nuetzlich. Das
script /usr/local/bin/jailping
bestehend aus finger $*@HOSTSYSTEM
gepaart mit einem fingerd, der read input; (ping ${input%^M} 2>&1)
fuer
Verbindungen aus den jails erlaubt, schafft Abhilfe. Am besten noch alias
ping='/usr/local/bin/jailping'
. Done.
Hostname im jail aendern verbieten mit jail_set_hostname_allow="NO"
in der
/etc/rc.conf
. Ein Sicherheitsfeature, was gern abgeschaltet wird (auch in meinem
jail-Server, wegen der Datenbanken, die es brauchen), ist sysvipc.
jail_sysvipc_allow
und postgresql geht.