Hier habe ich einen guten Artikel entdeckt, der beschreibt, welche Möglichkeiten alles Systemd bietet, um Prozesse einzuschränken: Flash Systems – Systemhärtung mit systemd.

Aus meiner Sicht sind folgende Optionen erstrebenswert, aber nicht bei allen Diensten lässt sich der Gurt so festzurren. Weil ich meine Systeme mit einem Read-only-Root betreibe (das bereits ProtectSystem=full entspricht) und die Dateisystemzugriffe über AppArmor begrenze, wären viele Optionen nicht notwendig, aber bisher sind mir noch keine Nachteile der Doppelung aufgefallen.

[Service]
CapabilityBoundingSet=

LockPersonality=true
MemoryDenyWriteExecute=true
NoNewPrivileges=true

PrivateDevices=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true

# strict: mounts the whole system read-only
# full: mount only /var as read-write
# true: mount /var and /etc as read-write
ProtectSystem=strict
# Make some paths writable
#ReadWritePaths=/…
ProtectHome=true

# Make sure that the process can only see PIDs and process details of itself,
# and the second option disables seeing details of things like system load and
# I/O etc
ProtectProc=invisible
ProcSubset=pid

RemoveIPC=true
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true

SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources @obsolete
# No network at all
#SystemCallFilter=~@network-io

Die Qualität der Einstellungen lässt sich mit systemd-analyze security ….service prüfen.

Für User-Units lassen sich nur diese Optionen nutzen:

[Service]
LockPersonality=true
NoNewPrivileges=true
MemoryDenyWriteExecute=true
RemoveIPC=true
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
SystemCallArchitectures=native
SystemCallFilter=@system-service

Rechte zur Nutzung von strace

Viele Programme geben im Fehlerfall keine aussagekräftigen Meldungen aus. Daher ist es oft hilfreich, dem Prozess mit strace zuzuschauen. Hierfür muss man folgende Einstellungen tätigen und natürlich ExecStart=/usr/bin/strace -AfvttTy -o /tmp/%p.st … setzen:

CapabilityBoundingSet=CAP_SYS_PTRACE
ReadWritePaths=/tmp
PrivateTmp=no
SystemCallFilter=@debug @privileged

Ermitteln der Lockerungen

Leider habe ich für die Einstellungen noch keinen Complain-Mode wie bei AppArmor gefunden. Daher gehe ich immer nach dem Ausschlussprinzip durch die obigen Einstellungen und schaue, welche für das Scheitern des Prozesses verantwortlich ist. Zuerst nehme ich immer CapabilityBoundingSet und SystemCallFilter raus und suche die Einstellungen, damit es funktioniert. Danach suche ich mit strace nach den Funktionen, die bei SystemCallFilter erlaubt werden müssen; der Befehl systemd-analyze syscall-filter gibt die vorhandenen Gruppen aus. Anhand derer ist meist auch zu erkennen, welche Rechte bei CapabilityBoundingSet notwendig sind; die Liste ist in capabilities(7) erklärt. Die Capabilities lassen sich auch leichter mit AppArmor und complain ermitteln.