Systemd Hardening Workshop

Aus Xinux Wiki
Zur Navigation springen Zur Suche springen

Vorwort

  • In diesem Workshop geht es darum einen systemd Dienst sicherer zumachen

Debuggen

  • Wir benutzen hierzu systemd-analyze
  • Dieses Tool analysiert die Sicherheit und die Sandboxing-Einstellungen der Dienste.
  • Der Befehl prüft auf verschiedene sicherheitsrelevante Diensteinstellungen
  • Er weist jeder einen numerischen „Exposure Level“-Wert zu, je nachdem, wie wichtig die Einstellung ist.
  • Anschließend berechnet es ein Gesamtexpositionsniveau für die gesamte Einheit durch eine Schätzung
  • Diese lieht im Bereich von 0,0 bis 10,0, die uns sagt, wie exponiert ein Dienst sicherheitstechnisch ist.
  • systemd-analyze security
UNIT                                 EXPOSURE PREDICATE HAPPY
cron.service                              9.6 UNSAFE    😨
dbus.service                              9.6 UNSAFE    😨
emergency.service                         9.5 UNSAFE    😨
getty@tty1.service                        9.6 UNSAFE    😨
rc-local.service                          9.6 UNSAFE    😨
rescue.service                            9.5 UNSAFE    😨
rsyslog.service                           9.6 UNSAFE    😨
ssh.service                               9.6 UNSAFE    😨
strongswan-starter.service                9.6 UNSAFE    😨
systemd-ask-password-console.service      9.4 UNSAFE    😨
systemd-ask-password-wall.service         9.4 UNSAFE    😨
systemd-fsckd.service                     9.5 UNSAFE    😨
systemd-initctl.service                   9.4 UNSAFE    😨
systemd-journald.service                  4.3 OK        🙂
systemd-logind.service                    2.6 OK        🙂
systemd-networkd.service                  2.9 OK        🙂
systemd-timesyncd.service                 2.1 OK        🙂
systemd-udevd.service                     8.0 EXPOSED   🙁
user@0.service                            9.8 UNSAFE    😨
user@1000.service                         9.4 UNSAFE    😨
webserver.service                         9.6 UNSAFE    😨

Vorgehensweise

  • Wir können nun Schritt für Schritt die Verbesserungen überprüfen, die an unserem systemd-Dienst vorgenommen wurden.
  • Wie Sie sehen können, sind jetzt mehrere Dienste als UNSAFE gekennzeichnet, was wahrscheinlich daran liegt, dass nicht alle Anwendungen die von systemd bereitgestellten Funktionen anwenden.

Einstieg

  • Beginnen wir mit einem einfachen Beispiel.
  • Wir wollen eine systemd-Unit erstellen, um den Befehl python3 -m http.server als Dienst zu starten:
[Unit]
Description=Simple Http Server
Documentation=https://docs.python.org/3/library/http.server.html

[Service]
Type=simple
ExecStart=/usr/bin/python3 -m http.server
ExecStop=/bin/kill -9 $MAINPID

[Install]
WantedBy=multi-user.target
  • Wir speichern diese Datei unter /etc/systemd/system/simplehttp.service ab

Überprüfung

  • systemd-analyze security | grep simp
simplehttp.service                        9.6 UNSAFE    😨

PrivateTmp

  • Es erstellt einen Dateisystem-Namensraum unter /tmp/systemd-private-*-[Unit-Name]-*/tmp
  • Dies wird anstelle eines gemeinsam genutzten /tmp oder /var/tmp benutzt.
  • Viele der mit Red Hat Enterprise Linux veröffentlichten Unit-Dateien enthalten diese Einstellung.
  • Dies entfernt die eine ganze Klasse von Schwachstellen im Zusammenhang mit der Vorhersage und Ersetzung von Dateien entfernt, die in /tmp verwendet werden.

Die Änderung

[Unit]
Description=Simple Http Server
Documentation=https://docs.python.org/3/library/http.server.html 

[Service]
Type=simple
ExecStart=/usr/bin/python3 -m http.server
ExecStop=/bin/kill -9 $MAINPID

#Secure Features
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

Neu einlesen und restart

  • systemctl daemon-reload && systemctl restart simplehttp.service

Neue Untersuchung

  • systemd-analyze security | grep simp
simplehttp.service                        9.2 UNSAFE    😨
Der Wert hat sich von 9.6 auf 9.2 verbessert

NoNewPrivileges

  • Es verhindert, dass der Dienst und zugehörige untergeordnete Prozesse Berechtigungen eskalieren.
NoNewPrivileges=true
Automatisches Editieren
SECURE='NoNewPrivileges=true'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        9.0 UNSAFE    😨

Namensräume einschränken

  • Es begrenzt alle oder eine Teilmenge von Namespaces auf den Dienst.
  • Die Direktive akzeptiert cgroup, ipc, net, mnt, pid, user und uts.
RestrictNamespaces=uts ipc pid user cgroup
  • Wie Sie oben sehen können, wurde der net-Namespace nicht festgelegt, da sich der Dienst an eine Netzwerkschnittstelle binden muss.
  • Das Isolieren von net von einem Netzwerkdienst macht es nutzlos.
  • SECURE='RestrictNamespaces=uts ipc pid user cgroup'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        8.9 EXPOSED   🙁

ProtectSystem=strict

  • Die erste Option zur Härtung eines Service ist ProtectSystem.
  • Diese kennt mehrere Werte: true oder yes lassen wir mal außen vor, da sie aus meiner Sicht zu schwach ist und wenig mit Härtung zu tun hat.
  • Wer Näheres wissen möchte kann die Man-Page systemd.exec befragen. Bleiben noch full und strict.
Option	Beschreibung
full	Mounted /usr, /boot und /etc als nicht beschreibbar.
strict	Mounted das gesamte Dateisystem (außer /dev, /proc und /sys) als nicht beschreibbar.

Umsetzung

  • SECURE='ProtectSystem=strict'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        8.7 EXPOSED   🙁

CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_DAC_READ_SEARCH

  • Das CapabilityBoundingSet sollte für normale Dienste auf einen leeren Wert gesetzt werden.
  • Zusätzlich mit der Option NoNewPrivileges verhindert es zuverlässig, dass ein Prozess seine eigenen Privilegien ändert oder eine der in den vorausgehenden Kapiteln gemachten Einschränkungen umgeht.

Umsetzung

  • SECURE='CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_DAC_READ_SEARCH'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        6.5 MEDIUM    😐

ProtectKernelTunables=yes

  • Die Option die sich auf das Dateisystem bezieht: ProtectKernelTunables
  • Sie macht /proc/sys, /sys, /proc/sysrq-trigger, /proc/latency_stats, /proc/acpi, /proc/timer_stats, /proc/fs und /proc/irq einfach nicht beschreibbar. *Damit kann der Service keine Kernel-Einstellungen mehr ändern. Für einen Webserver oder eine Datenbank ist das hoffentlich auch nicht nötig.

Umsetzung

  • SECURE='ProtectKernelTunables=yes'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        6.3 MEDIUM    😐

ProtectKernelModules=yes

  • Verhindert das Laden neuer Kernel-Module.
  • Hierfür wird zum einen das CapabilityBoundSet entsprechend modifiziert und zum anderen ein System-Call-Filter aktiviert, welcher das Aufrufen von Funktionen zur Modulverwaltung blockiert.
  • Diese Option kann häufig ohne weitere Nebenwirkungen gesetzt werden, da nur wenige Dienste wirklich Kernel-Module laden müssen.

Umsetzung

  • SECURE='ProtectKernelModules=yes'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        6.1 MEDIUM    😐

ProtectControlGroups=yes

  • Verhindert Schreibzugriff auf alle Einstellungen der Kernel-Control-Groups (unter /sys/fs/cgroup).
  • Die meisten Dienste (außer Container-Manager) brauchen diesen Zugriff nicht.
  • Systemd verwendet jedoch Control-Groups um einige Einschränkungen für die laufenden Dienste umzusetzen.
  • Daher sollte der Schreibzugriff fast immer mit ProtectControlGroups=yes blockiert werden, damit der Prozess nicht doch noch aus seinem Gefängnis entkommen kann.

Umsetzung

  • SECURE='ProtectControlGroups=yes'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        5.9 MEDIUM    😐

PrivateDevices=yes

  • Ein PrivateDevices=yes erzeugt für den Service ein eigenes /dev Verzeichnis
  • In diesem nur noch die für den Betrieb eines Linux-Programmes nötigen Geräte vorhanden sind:

Umsetzung

  • SECURE='PrivateDevices=yes'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        5.6 MEDIUM    😐

RestrictSUIDSGID=true

  • SECURE='RestrictSUIDSGID=true'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        5.4 MEDIUM    😐

IPAddressAllow=10.0.10.0/24

  • SECURE='IPAddressAllow=10.0.10.0/24'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        5.4 MEDIUM    😐

SystemCallFilter=yes

  • Mit dieser Option können einzelne System-Calls verboten werden.
  • Aufgrund der Menge und des durch die Weiterentwicklung von Linux dynamischen Angebots an Systemaufrufen sollte man es sich hier tunlichst verkneifen eigene Listen zu pflegen.
  • Systemd bietet hierfür einige Standardsets an Systemaufrufen an, welche man deaktivieren kann.
  • Die Liste findet sich in der Man-Page von systemd.exec.
  • Für den Normalbetrieb bringt systemd ein Set von Systemaufrufen mit, welches normalerweise für alle gängigen Dienste ausreicht:


  • SECURE='SystemCallErrorNumber=EPERM'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        4.0 OK        🙂


  • SECURE='SystemCallFilter=@system-service'
  • sed -ie "/#Secure Features/a$SECURE" /etc/systemd/system/simplehttp.service
  • systemctl daemon-reload && systemctl restart simplehttp.service
  • systemd-analyze security | grep simp
simplehttp.service                        4.0 OK        🙂

Läuft der Dienst überhaupt noch?

  • ss -lntp | grep 8000
LISTEN 0      5            0.0.0.0:8000      0.0.0.0:*    users:(("python3",pid=3333,fd=3)) 


Ergebnis

[Unit]
Description=Simple Http Server
Documentation=https://docs.python.org/3/library/http.server.html

[Service]
Type=simple
ExecStart=/usr/bin/python3 -m http.server
ExecStop=/bin/kill -9 $MAINPID

#Secure Features
SystemCallFilter=@system-service
IPAddressAllow=10.0.10.0/24
RestrictSUIDSGID=true
PrivateDevices=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_DAC_READ_SEARCH
ProtectSystem=strict
RestrictNamespaces=uts ipc pid user cgroup
NoNewPrivileges=true
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

Links