Notfall–sshd OOM–resistent
Dieser Artikel beschreibt, wie man einen SSH-Dienst mit maximaler Zuverlässigkeit betreibt. Ursprünglich war es ein Blogpost, der wegen seiner Länge (und Komplexität) als Artikel neu angelegt wurde.
In extremen Notfällen, wenn beispielsweise ein "normal" gestarteter sshd durch einen OOM-Kill (Out Of Memory) des Kernels beendet wurde, wünscht sich ein Admin gerne noch eine Login-Möglichkeit.
Hier zeigt sich der große Nachteil eines "normal" gestarteten Daemons (per SysV-Script oder vergleichbar), bei dem vielleicht noch ein PID-File geschrieben wird, es aber keine Instanz gibt, die über ein Ende des Daemons unmittelbar informiert wird. Tools wie monit verschlimmbessern die Lage, da es einen weiteren Prozess gibt, der aber trotzdem das Ende eines Daemons nicht direkt mitbekommt, sondern prüfen muß, ob der Daemon noch läuft.
ssh mit supervising
Einen einfachen Ausweg bietet die supervising-Methode, bei der ein Supervise-Prozess unmittelbar und sofort vom Ende eines als Kind laufenden Daemons vom Kernel informiert wird und diesen neu starten kann.
Die daemontools von DJB bieten Supervising von Prozessen. So sieht beispielhaft eine Prozesshierarchie mit einem supervised sshd aus:
/command/svscan /service/ \_ supervise sshd-sv \_ /usr/sbin/sshd -D -p 8234 -o PidFile=/var/run/sshd.pid
Das von supervise gestartete Script sieht so aus:
#!/bin/sh exec 2>&1 # don't respawn too fast /bin/sleep 1 exec /usr/sbin/sshd -D -p 8234 -o PidFile=/var/run/sshd.pid \ -o UseDNS=no
Da die supervise-Hierarchie über init mit Option "respawn" gestartet wird, ist somit auch der Restart von supervise selbst gesichert (beispielsweise bei den oben erwähnten Out-Of-Memory-Kills des Kernels). Bei SysV-init kann die Konfigurationszeile unmittelbar nach "si" (system init) eingetragen werden, so dass man sich bereits einloggen kann, während die Bootscripts noch gestartet werden. So sehen beispielhaft Konfigurationen für SysV-Init und Upstart aus:
# grep -B1 SV /etc/inittab si::sysinit:/sbin/rc sysinit SV:2345:respawn:/command/svscanboot3 # cat /etc/init/daemontools.conf description "DJB daemontools" start on filesystem stop on runlevel [06] respawn exec /command/svscanboot3 #
ssh mit supervising und ramfs
So zuverlässig sshd über supervise über svscan über ... init läuft, wenn das root-FS in Notfällen vom Kernel (oder von root per Magic Sysrq) RO-remountet wird, kann supervise keinen sshd mehr starten, da Schreibzugriff benötigt wird. Als einfacher Ausweg aus der Misere bietet sich hier ramfs an. Wenn supervise das run-Script gestartet hat, kann aber innerhalb des Scripts nicht einfach das ramfs gemountet werden, da dann mit svc über die control-Pipe nicht mehr supervise kontrolliert werden kann. Daher wird in dem folgenden run-Script nicht svc genutzt, sondern supervise direkt über ein echo in die control-Pipe beendet, damit ein "neues" supervise von svscan gestartet werden kann, das dann das ramfs im supervise-Verzeichnis nutzt.
#!/bin/sh exec 2>&1 # don't respawn too fast /bin/sleep 1 # if supervise is not a ramdisk try to mount it as ramdisk grep -q sshd-sv.supervise /proc/mounts || { /bin/rm -rf supervise-nativefs mv supervise supervise-nativefs && { if mkdir -p supervise then if mount -t ramfs /dev/ram0 supervise then echo -e 'dx\c' >supervise-nativefs/control else rmdir supervise mv supervise-nativefs supervise fi else mv supervise-nativefs supervise fi } } exec /usr/sbin/sshd -D -p 8234 \ -o PidFile=/var/run/sshd-sv.pid \ -o UseDNS=no