💾 Archived View for senioradmin.de › Debian11Runit.gmi captured on 2021-12-04 at 18:04:22. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-12-03)
-=-=-=-=-=-=-
Unter unixoiden Betriebssystemen - dazu zählen u. a. GNU/Linux, FreeBSD und OpenBSD - wird als erster Prozess immer ein Programm namens "init" gestartet. Dies ist im Betriebssystem-Kern, dem Kernel, so festgelegt. Das Programm init wiederum startet weitere Programme, wie z. B. Systemdienste, und stellt am Ende des Bootprozesses einen Login-Aufforderung bereit
Vereinfacht gesagt läuft das starten eines Unixartigen System so ab:
Bootloader (z.B. Grub) -> Kernel (z.B. der Linux-Kernel) -> init -> Login-Prompt
Vor einigen Jahren fand in den meisten Linux-Distributionen ein Wechsel des Init-Systems statt. Das zuvor benutzte Init-System "SysVinit" hat seinen Ursprung noch in der Urzeit von Unix. Dienste werden bei SysVInit durch Shellskripte gestartet, die sehr komplex werden können. Durch die sequentielle Abarbeitung dieser Skripte konnte ein System sehr lange zum starten brauchen. Dies erschien nicht mehr zeitgemäß. Es gab daher schon seit längerer Zeit Versuche, SysVInit durch etwas moderneres zu ersetzen.
Canonical versuchte es bei Ubuntu mit dem System "Upstart". Schon lange zuvor hat der IT-Professor Daniel J. Bernstein (auch bekannt als "djb") mit den "daemontools" versucht, das Init-System zu verbessern.
2010 programmierte Lennart Poettering - Angestellter bei Red Hat - die Software "systemd". Diese soll nicht nur als Init-System dienen, sondern ein komplettes Framework bereitstellen, welches der Verwaltung von Linux-Systemen dient. Systemd startet nicht nur Dienste, sondern stellt auch Sockets zur Verfügung. Außerdem stellt systemd Dienste mit eigenen Dienstprogrammen als Ersatz zu den traditionellen Unixprogrammen bereit. So existieren z.B. systemd-networkd, systemd-logind, systemd-journald (als Ersatz für syslog), systemd-resolved (Namensauflösung), systemd-timesyncd, usw. Und die Zahl wächst ständig weiter.
Im letzten Jahrzehnt hat sich systemd bei den meisten Distributionen als Standard durchgesetzt. Bei Debian ist es seit Version 8 das Standard Init-System.
Systemd ist, wie gesagt, nicht nur ein Init-System, sondern nimmt eine Vielzahl von Aufgaben wahr. Wenn man sich vor Augen führt, welche Aufgaben ein modernes Desktop-Betriebssystem zu leisten hat, dann macht es sicher Sinn,diese Aufgaben in einem integrierten System zusammenzufassen. Im allgemeinen interessieren sich Anwenderinnen und Anwender nicht für die einzelnen internen Dienste, die auf dem Rechner laufen. Sie möchten, dass der Rechner funktioniert und schnell ist. Das ist vollkommen legitim und für diesen Zweck ist systemd auch in Ordnung.
In der IT sprechen wir oft von "Anwendungsfällen" und der Anwendungsfall "Desktop" ist nur einer von vielen. Im Bereich der Systemadministration kommt es oft darauf an, das System (Server, Router usw.) stabil und sicher zu halten. Zwei Unix-Grundprinzipien um dies zu erreichen heißen
Keep it small and simple
und
Do one thing and do it well
Dies hat auch den Sinn, all zu große Komplexität zu vermeiden, denn Komplexität ist der Feind der Sicherheit. Nun muss man konstatieren, dass systemd diese Prinzipien nur wenig beachtet. Die Software besteht aus über 1,2 Mio Zeilen Code (Stand 2019) und sie hat es - wenig überraschend - schon zu Schlagzeilen durch spektakuläre
gebracht.
Eine Software, die so nahe am Betriebssystem arbeitet, sollte daher - wenn Wert auf Sicherheit gelegt wird - "small and simple" sein, um Sicherheitslücken möglichst zu vermeiden.
Wie schon geschrieben, wurden im Laufe der letzten 20 Jahre einige Versuche unternommen, das Init-System zu modernisieren. Einige davon fanden durchaus etwas Verbreitung. Das System "OpenRC" könnte man als evolutionäre Weiterentwicklung von SysVInit betrachten. Dieses System kommt vor allem bei Alpine Linux und Gentoo zum Einsatz.
Auf Basis der o.g. daemontools von djb entanden weitere Systeme, wie s6 oder eben
. Diese daemontools-inspirierten Init-Systeme ähneln sich in der Struktur und Anwendung, sind aber unterschiedlich komplex.
Das System runit ist vor allem auf Einfachheit und eine kleine Codebasis ausgelegt. Dies ist schonmal eine gute Voraussetzung um ein sicheres System aufzubauen. Es besteht aus mehreren kleinen Programmen und kennt per default 3 "stages":
Stage 1 - Systeminitiierung
Stage 2 - Dienste starten
Stage 3 - Herunter fahren oder neu starten
Die einzelnen Programme sind:
Generell besteht ein Init-System aus
Runit ist sehr minimal gehalten und hat keinen ausgewachsenen Service Manager. Zum starten und stoppen wird `sv` benutzt.
Ich gehe hier von einer minimalen Systeminstallation von Debian 11 aus, die mit einem "Netinst" ISO Image durchgeführt wurde. Wie eine solche minimale Installation von Debian durchgeführt wird ist nicht Teil dieses Beitrags. Wichtig ist nur: in der Software-Auswahl sollte alles abgewählt werden.
Nach der Anmeldung am System als root werden zuerst die runit Pakete installiert
apt install runit runit-init
Da dies das Init-System austauscht, erfolgt eine Sicherheitsabfrage, bei der `Yes, do as I say!` eingeben muss. Danach wird das System mit `reboot` neu gestartet.
Dann erneut Login als `root`. Runit sollte bereits rennen, aber es muss noch etwas aufgeräumt werden. Als erstes wird systemd deinstalliert
apt --purge remove systemd
In der Regel wird ein Login-Manager benötigt.
apt install libpam-elogind
Schließlich wird mit APT-Präferenzen dafür gesorgt, dass sich systemd nicht wieder durch die Hintertür (durch irgendwelche Abhängigkeiten) rein schleicht
cat << EOF > /etc/apt/preferences.d/00systemd Package: systemd Pin: origin "" Pin-Priority: -1 EOF
Nun läuft runit zwar, aber außer, dass es als init dient und getty startet und überwacht, tut es noch nicht viel. Genau wie sysvinit startet es zwar auch Dienste über die Skripte in `/etc/init.d`, aber das könnte man auch mit SysVinit haben. Um die Vorteile von runit mit der Supervision von Diensten zu nutzen müssen diese Dienste im "runit Stil" gestartet werden. Glücklicherweise ist dies sehr einfach. Runit Dienste benötigen, im Gegensatz zu SysVInit, meist nur ein ganz kurzes Startskript. Die Dienste laufen dabei im Vordergrund und dürfen nicht in den Hintergrund forken (also kein "daemonizing"). Auch Krücken wie "start-stop-daemon" werden mit runit nicht mehr gebraucht.
Als erstes wird der Dienst `rsyslogd` zu einem runit Dienst "konvertiert"
# runit Serviceverzeichnis anlegen mkdir /etc/sv/rsyslogd # run Datei erzeugen cat << EOF >/etc/sv/rsyslogd/run #!/bin/sh exec /usr/sbin/rsyslogd -n EOF # Ausführbar machen chmod a+x /etc/sv/rsyslogd/run # SysV rsyslogd stoppen /etc/init.d/rsyslog stop # SysV Dienst deaktivieren update-rc.d -f rsyslog remove # runit Dienst aktivieren ln -s /etc/sv/rsyslogd /etc/runit/runsvdir/default/
Wie zu sehen ist, sorgt das anlegen eines Symlinks vom Serviceverzeichnis in das runsvdir default Verzeichnis dafür, dass der Dienst als "aktiv" gesetzt und auch gleich gestartet wird.
Dbus benötigt neben "run" eine weitere Datei namens "check"
mkdir /etc/sv/dbus cat << EOF > /etc/sv/dbus/check #!/bin/sh exec dbus-send --system / org.freedesktop.DBus.Peer.Ping >/dev/null 2>&1 EOF chmod a+x /etc/sv/dbus/check cat << EOF > /etc/sv/dbus/run #!/bin/sh dbus-uuidgen --ensure=/etc/machine-id [ ! -d /run/dbus ] && install -m755 -g 81 -o 81 -d /run/dbus exec dbus-daemon --system --nofork --nopidfile EOF chmod a+x /etc/sv/dbus/run /etc/init.d/dbus stop update-rc.d -f dbus remove # Es folgte eine Fehlermeldung, davon nicht irritieren lassen: insserv: FATAL: service dbus has to be enabled to use service elogind ln -s /etc/sv/dbus /etc/runit/runsvdir/default/
mkdir /etc/sv/elogind cat << EOF > /etc/sv/elogind/run #!/bin/sh sv check dbus >/dev/null || exit 1 exec /usr/lib/elogind/elogind EOF chmod a+x /etc/sv/elogind/run update-rc.d -f elogind remove ln -s /etc/sv/elogind /etc/runit/runsvdir/default/
Weitere Dienste zu "runit-fizieren" sollte kein Problem sein. Im Zweifelsfall kann auch bei der auf Arch basierenden Distro
nachgesehen werden. Dieses Repo beinhaltet viele Beispiele für runit-Dienstskripte
Runit bringt mit svlogd einen Logging daemon mit, wwelcher Autorotate beherrscht. Dazu wird im Service-Verzeichnis ein Verzeichnis `log` angelegt. In diesem wir wiederum eine Datei ausführbares Skript `run` angelegt, welches svlogd startet. Hier ein Beispiel
mkdir -p /etc/sv/<dienstname>/log cat << EOF > /etc/sv/<dienstname>/log/run #!/bin/sh S="dienstname" mkdir -p /var/log/runit/$S chown _runit-log:adm /var/log/runit/$S chmod 750 /var/log/runit/$S exec chpst -u _runit-log svlogd -tt /var/log/runit/$S EOF chmod a+x /etc/sv/<dienstname>/log/run
Damit svlogd loggen kann, muss der Dienst seine Meldungen auf stdout ausgeben. Einige Dienste benötigen dafür eine zusätzliche Konfiguration.
Ich habe hier gezeigt, wie systemd auf Debian 11 mit dem Init-Dienst runit ersetzt werden kann. Da runit ein erprobtes, sehr schlankes und sicheres System ist, ist es damit möglich, auch Debian ein ganzes Stück sicherer und zuverlässiger zu konfigurieren. Dies trifft sowohl für Desktops als auch (erst recht) für Server zu. Die Supervision sorgt dafür, dass Dienste überwacht werden.
Ich will nicht verschweigen, dass runit jedoch bei komplexeren Szenarien an seine Grenzen kommt. Dafür ist dann möglicherweise das verwandte und ebenfalls auf den djb daemontools basierende System
geeignet, welches jedoch eine deutlich steilere Lernkurve hat.