Jeder Service auf einem Linux-Server ist eine potentielle Angriffsfläche. Ein kompromittierter Webserver, der als root läuft und Zugriff auf das gesamte Dateisystem hat, wird schnell zur Katastrophe. Systemd bietet dutzende Sicherheitsoptionen, die Services in Sandboxen isolieren, Dateisystemzugriffe einschränken und Ressourcen begrenzen — ohne die Anwendung selbst ändern zu müssen.
systemd-analyze security: Den Status quo bewerten
Bevor Sie Härtungsmaßnahmen implementieren, analysieren Sie den aktuellen Zustand:
systemd-analyze security
Die Ausgabe zeigt für jeden Service eine Sicherheitsbewertung von 0.0 (sicher) bis 10.0 (unsicher):
UNIT EXPOSURE PREDICATE HAPPY
nginx.service 9.6 UNSAFE 😨
postgresql.service 9.2 UNSAFE 😨
sshd.service 9.6 UNSAFE 😨
prometheus.service 9.6 UNSAFE 😨
Ein Score von 9+ bedeutet: Der Service hat praktisch keine Einschränkungen. Für eine detaillierte Analyse eines einzelnen Services:
systemd-analyze security nginx.service
Das zeigt alle Sicherheitsdirektiven und ihren aktuellen Status — rot markierte Einträge sind aktive Risiken.
Dateisystem-Schutz
ProtectSystem
ProtectSystem macht Systemverzeichnisse schreibgeschützt:
[Service]
# "strict" macht das gesamte Dateisystem read-only
# Ausnahmen über ReadWritePaths definieren
ProtectSystem=strict
ReadWritePaths=/var/log/nginx /var/cache/nginx /run/nginx
| Wert | Wirkung |
|---|---|
true | /usr und /boot read-only |
full | Wie true + /etc read-only |
strict | Gesamtes Dateisystem read-only (empfohlen) |
ProtectHome
Verhindert Zugriff auf Home-Verzeichnisse:
[Service]
# true: /home, /root, /run/user unzugänglich
ProtectHome=true
# read-only: Lesezugriff erlaubt, kein Schreiben
# ProtectHome=read-only
# tmpfs: Leere tmpfs-Mounts statt der echten Verzeichnisse
# ProtectHome=tmpfs
PrivateTmp
Gibt dem Service ein eigenes /tmp-Verzeichnis, isoliert von allen anderen Prozessen:
[Service]
PrivateTmp=true
Ohne PrivateTmp teilen sich alle Services das gleiche /tmp — ein klassischer Vektor für Symlink-Attacken und Privilege Escalation.
ReadOnlyPaths und InaccessiblePaths
Für granulare Kontrolle:
[Service]
# Bestimmte Pfade schreibgeschützt
ReadOnlyPaths=/etc /var/lib/shared-data
# Bestimmte Pfade komplett unzugänglich
InaccessiblePaths=/root /home /mnt /media
# Nur bestimmte Pfade beschreibbar (mit ProtectSystem=strict)
ReadWritePaths=/var/lib/myapp /var/log/myapp /run/myapp
Kernel- und Geräte-Schutz
ProtectKernelTunables
Verhindert Änderungen an /proc/sys, /sys und ähnlichen Kernel-Interfaces:
[Service]
ProtectKernelTunables=true
Ein kompromittierter Service kann damit keine Kernel-Parameter ändern — kein sysctl-Manipulation, kein Deaktivieren von ASLR oder ähnliche Angriffe.
ProtectKernelModules
Verhindert das Laden und Entladen von Kernel-Modulen:
[Service]
ProtectKernelModules=true
ProtectKernelLogs
Blockiert den Zugriff auf den Kernel-Log-Ring-Buffer:
[Service]
ProtectKernelLogs=true
ProtectControlGroups
Macht die cgroup-Hierarchie schreibgeschützt:
[Service]
ProtectControlGroups=true
PrivateDevices
Erstellt eine minimale /dev-Umgebung ohne physische Geräte:
[Service]
PrivateDevices=true
Der Service sieht nur /dev/null, /dev/zero, /dev/full, /dev/random und /dev/urandom — keinen Zugriff auf Festplatten, USB-Geräte oder andere Hardware.
Capabilities einschränken
Linux Capabilities unterteilen die root-Rechte in einzelne Privilegien. Statt einem Service vollen root-Zugriff zu geben, können Sie genau die Capabilities zuweisen, die er benötigt:
CapabilityBoundingSet
[Service]
# Nur die nötigen Capabilities erlauben
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_DAC_READ_SEARCH
# Alle Capabilities entfernen (für Services ohne root-Bedarf)
CapabilityBoundingSet=
Wichtige Capabilities:
| Capability | Erlaubt | Typische Services |
|---|---|---|
CAP_NET_BIND_SERVICE | Ports < 1024 binden | nginx, Apache |
CAP_DAC_READ_SEARCH | Dateiberechtigungen umgehen (lesen) | Backup-Agents |
CAP_NET_RAW | Raw Sockets | Monitoring (Ping) |
CAP_SYS_PTRACE | Prozesse debuggen | Debugging-Tools |
CAP_CHOWN | Datei-Eigentümer ändern | FTP-Server |
AmbientCapabilities
Für Services, die als Nicht-Root-User laufen, aber bestimmte Capabilities brauchen:
[Service]
User=www-data
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
Privilege Escalation verhindern
NoNewPrivileges
Die wichtigste einzelne Sicherheitsdirektive — verhindert, dass der Prozess oder seine Kinder jemals mehr Privilegien erhalten als beim Start:
[Service]
NoNewPrivileges=true
Mit NoNewPrivileges=true funktionieren SUID-Binaries und Capability-Escalation nicht mehr. Das verhindert eine ganze Klasse von Privilege-Escalation-Angriffen.
PrivateUsers
Erstellt einen eigenen User-Namespace — der Service sieht nur sich selbst:
[Service]
PrivateUsers=true
RestrictSUIDSGID
Verhindert das Setzen von SUID/SGID-Bits:
[Service]
RestrictSUIDSGID=true
Netzwerk-Isolation
RestrictAddressFamilies
Beschränkt die nutzbaren Netzwerk-Protokollfamilien:
[Service]
# Nur IPv4, IPv6 und Unix-Sockets erlauben
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
# Für Services ohne Netzwerkbedarf
RestrictAddressFamilies=AF_UNIX
IPAddressDeny und IPAddressAllow
Firewall auf Service-Ebene:
[Service]
# Nur lokales Netz und Loopback erlauben
IPAddressAllow=127.0.0.0/8 192.168.0.0/16
IPAddressDeny=any
PrivateNetwork
Komplette Netzwerk-Isolation — der Service sieht nur das Loopback-Interface:
[Service]
# Nur für Services ohne Netzwerkbedarf
PrivateNetwork=true
Syscall-Filtering
SystemCallFilter
Beschränkt die nutzbaren Systemaufrufe auf das Minimum:
[Service]
# Systemd bietet vordefinierte Gruppen
SystemCallFilter=@system-service
# Alternativ: Gefährliche Syscalls explizit blockieren
SystemCallFilter=~@mount @clock @debug @module @reboot @swap @raw-io
Vordefinierte Syscall-Gruppen:
| Gruppe | Beschreibung |
|---|---|
@system-service | Basis-Set für normale Services |
@network-io | Netzwerk-Operationen |
@file-system | Dateisystem-Operationen |
@mount | Mount/Unmount |
@clock | Systemzeit ändern |
@debug | Debugging (ptrace) |
@module | Kernel-Module laden |
@reboot | System neustarten |
SystemCallArchitectures
Beschränkt die erlaubten CPU-Architekturen für Syscalls:
[Service]
# Nur native Architektur (verhindert 32-Bit-Exploitation auf 64-Bit)
SystemCallArchitectures=native
Resource Limits: CPUQuota und MemoryMax
Neben Security-Sandboxing bietet systemd auch Ressourcenbegrenzung über cgroups:
CPU begrenzen
[Service]
# Maximal 200% CPU (2 Kerne auf einem Multi-Core-System)
CPUQuota=200%
# CPU-Gewichtung (relativ zu anderen Services)
CPUWeight=50
Speicher begrenzen
[Service]
# Maximaler RAM-Verbrauch
MemoryMax=2G
# Weiches Limit (Service darf darüber, wird aber bevorzugt zurückgefahren)
MemoryHigh=1G
# Swap begrenzen
MemorySwapMax=500M
I/O begrenzen
[Service]
# I/O-Gewichtung
IOWeight=50
# Maximaler Durchsatz pro Gerät
IOReadBandwidthMax=/dev/sda 100M
IOWriteBandwidthMax=/dev/sda 50M
Prozesse und Tasks begrenzen
[Service]
# Maximale Anzahl Prozesse/Threads
TasksMax=64
# Maximale Anzahl offener Dateien
LimitNOFILE=65536
Vollständiges Hardening-Beispiel: nginx
# /etc/systemd/system/nginx.service.d/hardening.conf
[Service]
# Dateisystem
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ReadWritePaths=/var/log/nginx /var/cache/nginx /run/nginx
# Kernel
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
# Capabilities
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
# Netzwerk
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
# Syscalls
SystemCallFilter=@system-service
SystemCallArchitectures=native
# Weitere Härtung
RestrictRealtime=true
RestrictSUIDSGID=true
ProtectClock=true
LockPersonality=true
MemoryDenyWriteExecute=true
RemoveIPC=true
# Resource Limits
MemoryMax=1G
TasksMax=512
Anwenden:
systemctl daemon-reload
systemctl restart nginx
# Sicherheit erneut prüfen
systemd-analyze security nginx.service
# Erwartung: Score sinkt von 9.6 auf ca. 2.0-3.0
Eigene Timer als Cron-Ersatz
Systemd Timer bieten gegenüber cron Vorteile: Logging über journald, Abhängigkeiten, Randomized Delay und die gleichen Sandboxing-Optionen wie Services.
Timer erstellen
# /etc/systemd/system/backup-daily.timer
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=*-*-* 02:00:00
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup-daily.service
[Unit]
Description=Daily backup job
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backup
Group=backup
# Hardening (gleiche Optionen wie bei Services)
ProtectSystem=strict
ReadWritePaths=/var/backup /var/log/backup
ProtectHome=true
PrivateTmp=true
NoNewPrivileges=true
MemoryMax=4G
systemctl enable --now backup-daily.timer
systemctl list-timers backup-daily.timer
Timer-Varianten
| Direktive | Beispiel | Beschreibung |
|---|---|---|
OnCalendar | daily, weekly, *-*-* 02:00:00 | Kalenderbasiert (wie cron) |
OnBootSec | 5min | Nach Systemstart |
OnUnitActiveSec | 1h | Nach letzter Aktivierung |
RandomizedDelaySec | 30min | Zufälliger Delay (Last verteilen) |
Persistent | true | Verpasste Läufe nachholen |
Hardening schrittweise einführen
Aktivieren Sie Härtungsoptionen nicht alle auf einmal — gehen Sie schrittweise vor:
- Phase 1:
ProtectSystem=strict,ProtectHome=true,PrivateTmp=true - Phase 2:
NoNewPrivileges=true,CapabilityBoundingSet - Phase 3:
SystemCallFilter,RestrictAddressFamilies - Phase 4:
MemoryMax,CPUQuota,TasksMax
Nach jeder Phase: Service testen, Logs auf Fehler prüfen, systemd-analyze security erneut ausführen.
Monitoring mit DATAZONE Control
DATAZONE Control überwacht die Systemd-Security-Konfiguration aller verwalteten Server: Security-Scores, aktive Hardening-Direktiven und Abweichungen von der Baseline fließen in das zentrale Compliance-Dashboard. Automatische Alerts warnen, wenn ein neuer Service ohne Hardening installiert wird oder ein Update Sicherheitsoptionen zurücksetzt.
Häufig gestellte Fragen
Kann Hardening einen Service kaputt machen?
Ja. Zu restriktive Einstellungen verhindern, dass der Service auf benötigte Ressourcen zugreift. Testen Sie Änderungen immer in einer Testumgebung und prüfen Sie journalctl -u <service> auf Fehlermeldungen.
Funktioniert Hardening mit Docker-Containern?
Systemd-Hardening gilt für den Docker-Daemon-Service, nicht für die Container selbst. Container haben ihr eigenes Isolation-Modell (Namespaces, cgroups). Beide Ansätze ergänzen sich.
Wie finde ich heraus, welche Capabilities ein Service braucht?
Starten Sie mit CapabilityBoundingSet= (keine Capabilities) und beobachten Sie die Fehlermeldungen im Journal. Fügen Sie dann die benötigten Capabilities einzeln hinzu.
Sie möchten Ihre Linux-Server systematisch härten? Kontaktieren Sie uns — wir implementieren Systemd-Hardening und überwachen die Compliance mit DATAZONE Control.
Mehr zu diesen Themen:
Weitere Artikel
Backup-Strategie für KMU: Proxmox PBS + TrueNAS als zuverlässiges Backup-Konzept
Backup-Strategie für KMU mit Proxmox PBS und TrueNAS: 3-2-1-Regel umsetzen, PBS als primäres Backup-Target, TrueNAS-Replikation als Offsite-Kopie, Retention Policies und automatisierte Restore-Tests.
OPNsense Suricata Custom Rules: Eigene IDS/IPS-Signaturen schreiben und optimieren
Suricata Custom Rules auf OPNsense: Rule-Syntax, eigene Signaturen für interne Services, Performance-Tuning, Suppress-Lists und EVE-JSON-Logging.
Grafana + Prometheus: IT-Monitoring-Stack aufbauen und konfigurieren
Grafana und Prometheus Stack aufbauen: node_exporter, Proxmox-Exporter, SNMP-Exporter für OPNsense, Dashboards erstellen, Alerting-Regeln, Retention und Vergleich mit Zabbix.