feingranulare Rechte jenseits von Root und sudo
Linux Capabilities teilen die traditionelle Root-Berechtigung in über vierzig einzelne Bits auf, sodass ein Prozess genau ein Recht bekommt, etwa einen privilegierten Port zu öffnen, ohne den gesamten Rest von Root mitzuerben. Das ist der Unterschied zwischen einem Least-Privilege-Design und einem Server, der bei jedem kompromittierten Dienst sofort vollen Root-Zugriff verliert.
Inhaltsverzeichnis
- 1. Warum "Root oder nichts" ein veraltetes Modell ist
- 2. Wie Linux Capabilities funktionieren: Bitmasken statt Root
- 3. Die wichtigsten Capabilities im Überblick
- 4. Capabilities einem Programm zuweisen mit setcap
- 5. Capabilities eines laufenden Prozesses inspizieren
- 6. Capabilities in systemd-Services einschränken
- 7. Capabilities in Containern: Docker und die Standardliste
- 8. Typische Fehler und Debugging von Capability-Problemen
- 9. Capabilities im Vergleich zu SUID, sudo und vollem Root
- 10. Zusammenfassung
- 11. FAQ
1. Warum "Root oder nichts" ein veraltetes Modell ist
Traditionell kennt Linux nur eine binäre Unterscheidung: Ein Prozess läuft entweder als Root mit vollem Zugriff auf jede privilegierte Operation, oder er läuft als normaler Benutzer und darf gar nichts Privilegiertes tun. Für viele reale Anwendungsfälle ist das grob unpassend. Ein Webserver, der Port 80 binden muss, braucht dafür genau ein privilegiertes Recht, bekommt in der klassischen Welt aber gleich vollen Root-Zugriff auf das gesamte System, inklusive Dateisystem-Mounts, Kernel-Modul-Laden und Prozess-Kontrolle über jeden anderen Benutzer.
Linux Capabilities lösen genau dieses Problem, indem sie die traditionellen Root-Rechte in über vierzig einzelne, unabhängig vergebbare Fähigkeiten aufteilen. Ein Prozess kann mit Linux Capabilities genau die eine Fähigkeit bekommen, die er braucht, etwa das Binden privilegierter Ports, ohne alle anderen Root-Rechte zu erben. Für Produktivserver mit PHP-FPM, Nginx oder spezialisierten Netzwerkdiensten ist das der entscheidende Baustein für ein echtes Least-Privilege-Design, bei dem ein kompromittierter Prozess eben nicht automatisch das ganze System übernimmt.
2. Wie Linux Capabilities funktionieren: Bitmasken statt Root
Technisch verwaltet der Kernel für jeden Prozess mehrere Capability-Sets, die als Bitmasken abgebildet werden. Das Permitted Set enthält alle Capabilities, die ein Prozess grundsätzlich annehmen darf. Das Effective Set enthält die Capabilities, die der Kernel bei der aktuellen Systemaufruf-Prüfung tatsächlich berücksichtigt. Das Inheritable Set steuert, welche Capabilities bei einem execve-Aufruf an ein neues Programm weitergegeben werden können. Diese Trennung erlaubt es, dass ein Prozess eine Capability zwar besitzt, sie aber gezielt deaktiviert, bis sie tatsächlich gebraucht wird.
Ein zentraler Unterschied zu SUID-Binaries: Während ein SUID-Programm bei jedem Start vollständig zur Datei-Eigentümer-Identität wechselt, meist Root, aktivieren Linux Capabilities nur exakt die benötigten Bits, während die Prozess-Identität selbst unverändert bleibt. Ein Programm mit der Capability CAP_NET_BIND_SERVICE kann privilegierte Ports binden, hat aber keinerlei Zugriff auf andere Root-Operationen wie das Setzen der Systemzeit oder das Laden von Kernel-Modulen, selbst wenn ein Angreifer den Prozess vollständig kompromittiert.
3. Die wichtigsten Capabilities im Überblick
Von den über vierzig definierten Linux Capabilities sind für den Serveralltag nur eine Handvoll wirklich relevant. CAP_NET_BIND_SERVICE erlaubt das Binden von Ports unterhalb von 1024, klassisch für Webserver und Mailserver. CAP_NET_ADMIN erlaubt Netzwerkkonfiguration wie das Ändern von Routing-Tabellen. CAP_SYS_ADMIN ist die am weitesten reichende Capability und bündelt so viele unterschiedliche Operationen, dass sie in der Praxis fast wie Root selbst wirkt, weshalb sie mit besonderer Vorsicht vergeben werden sollte.
# List all capabilities the current shell process holds
capsh --print
# Show the human-readable name of a specific capability bit
capsh --decode=0000000000003000
# List capabilities available on the system with short descriptions
man 7 capabilities | grep -A1 '^ CAP_' | head -40
# Common capabilities relevant to web and database servers:
# CAP_NET_BIND_SERVICE — bind ports below 1024 (e.g. port 80/443)
# CAP_NET_ADMIN — configure network interfaces and routing
# CAP_SYS_ADMIN — broad admin operations, avoid unless required
# CAP_DAC_OVERRIDE — bypass file permission checks
# CAP_CHOWN — change file ownership regardless of permissions
# CAP_SETUID/CAP_SETGID — change process UID/GID
Wichtig ist, CAP_SYS_ADMIN nicht als Standardlösung für "irgendwas funktioniert nicht" zu vergeben, wie es in vielen Docker-Anleitungen im Internet leider vorkommt. Diese Capability umfasst so viele unterschiedliche privilegierte Operationen, dass sie faktisch kaum weniger riskant ist als volles Root, und untergräbt damit den gesamten Sinn von Linux Capabilities als feingranulares Berechtigungsmodell.
4. Capabilities einem Programm zuweisen mit setcap
Der Befehl setcap weist einer ausführbaren Datei dauerhaft eine oder mehrere Linux Capabilities zu, die im erweiterten Dateisystem-Attribut der Datei gespeichert werden. Das klassische Beispiel: Ein PHP-FPM- oder Node.js-Prozess, der Port 80 direkt binden soll, ohne dafür als Root gestartet zu werden.
# Grant a binary the ability to bind privileged ports, nothing else
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/node
# Verify which capabilities are attached to a binary
getcap /usr/bin/node
# /usr/bin/node cap_net_bind_service=ep
# Remove a previously assigned capability
sudo setcap -r /usr/bin/node
# Assign multiple capabilities at once, comma-separated
sudo setcap 'cap_net_bind_service,cap_net_raw=+ep' /usr/sbin/my-network-tool
# Run the binary as a non-root user afterwards — port 80 binding still works
sudo -u www-data /usr/bin/node server.js
Nach dem setcap-Aufruf kann der Prozess als normaler, unprivilegierter Benutzer laufen und trotzdem den privilegierten Port binden. Diese Kombination aus non-root Prozessidentität und einer präzise zugeschnittenen Linux Capability reduziert die Angriffsfläche massiv im Vergleich zu einem klassischen Setup, bei dem der gesamte Prozess als Root gestartet und danach mühsam Rechte abgegeben werden müssen.
5. Capabilities eines laufenden Prozesses inspizieren
Für Audits und Debugging ist es wichtig, genau zu sehen, welche Linux Capabilities ein bereits laufender Prozess tatsächlich besitzt, unabhängig davon, was auf der Binary-Datei per setcap konfiguriert wurde. Der Kernel legt diese Information direkt unter /proc/PID/status ab, in vier separaten Hex-Bitmasken für die verschiedenen Capability-Sets.
# Inspect capability sets of a running process by PID
grep -i cap /proc/$(pgrep -f nginx | head -1)/status
# Decode the raw hex bitmask into readable capability names
capsh --decode=0000000000002400
# List capabilities for all processes owned by www-data
for pid in $(pgrep -u www-data); do
echo "PID $pid: $(grep CapEff /proc/$pid/status)"
done
# Compare intended (setcap) vs. actual (runtime) capabilities
getcap /usr/sbin/nginx
grep CapEff /proc/$(pgrep -f 'nginx: master' | head -1)/status
Eine Diskrepanz zwischen der über getcap konfigurierten und der über /proc/PID/status tatsächlich aktiven Capability deutet meist darauf hin, dass der Prozess die Capability zur Laufzeit selbst über prctl abgegeben hat, ein übliches Muster in sicherheitsbewusster Serversoftware, die Rechte nach der Initialisierung freiwillig reduziert.
6. Capabilities in systemd-Services einschränken
Für selbst geschriebene systemd-Services ist die direkte Deklaration im Unit-File der sauberste Weg, Linux Capabilities zu vergeben, ohne setcap manuell auf die Binary anwenden zu müssen. systemd bietet mit AmbientCapabilities und CapabilityBoundingSet zwei komplementäre Direktiven für genau diesen Zweck.
# /etc/systemd/system/my-app.service
[Unit]
Description=Custom application binding a privileged port
After=network.target
[Service]
Type=simple
User=appuser
Group=appuser
ExecStart=/opt/my-app/bin/server
# Grant exactly one capability at process start, without setcap on the binary
AmbientCapabilities=CAP_NET_BIND_SERVICE
# Restrict which capabilities this service could ever gain, even via setuid
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
# Additional hardening commonly paired with capability restrictions
NoNewPrivileges=true
ProtectSystem=strict
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Der entscheidende Vorteil dieser Methode: CapabilityBoundingSet wirkt als harte Obergrenze für den gesamten Prozessbaum des Services, selbst wenn eine untergeordnete Komponente versucht, über andere Wege zusätzliche Rechte zu erlangen. In Kombination mit NoNewPrivileges wird verhindert, dass der Service jemals mehr Linux Capabilities annimmt, als im Unit-File explizit deklariert wurden, unabhängig davon, was das ausgeführte Programm selbst versucht.
7. Capabilities in Containern: Docker und die Standardliste
Container-Runtimes wie Docker starten Container standardmäßig mit einer eingeschränkten Auswahl an Linux Capabilities, nicht mit vollem Root, auch wenn der Prozess im Container als UID 0 läuft. Diese Standardliste umfasst etwa 14 Capabilities, deutlich weniger als die über vierzig verfügbaren, und lässt sich sowohl weiter einschränken als auch gezielt erweitern.
Für die meisten Webanwendungs-Container ist selbst die Docker-Standardliste noch zu großzügig. Ein PHP-FPM- oder Node.js-Container, der keine Netzwerkkonfiguration und keine Dateisystem-Berechtigungsänderungen benötigt, sollte mit --cap-drop=ALL starten und dann nur die tatsächlich benötigten Linux Capabilities gezielt mit --cap-add wieder hinzufügen. Dieses Muster reduziert die Angriffsfläche eines kompromittierten Containers erheblich, weil selbst ein Ausbruch aus dem Prozess-Kontext nicht automatisch weitreichende Systemrechte mitbringt.
8. Typische Fehler und Debugging von Capability-Problemen
Der häufigste Fehler bei der Arbeit mit Linux Capabilities ist, eine fehlgeschlagene privilegierte Operation fälschlich als generelles Berechtigungsproblem zu interpretieren und dann reflexartig CAP_SYS_ADMIN oder gar volles Root zu vergeben, statt die konkrete, fehlende Capability zu identifizieren. Das Kernel-Log über dmesg oder Audit-Logs zeigen bei aktiviertem Auditing exakt, welche Capability bei welchem Systemaufruf fehlte.
# Enable auditd rules to log capability check failures
sudo auditctl -a always,exit -F arch=b64 -S capset -k cap_debug
# Search recent audit log for capability-related denials
sudo ausearch -k cap_debug --start recent
# Trace which capability a process needs using strace on the failing syscall
strace -f -e trace=%network ./my-app 2>&1 | grep -i "eperm\|eacces"
# Common mistake: file was copied/rebuilt after setcap, losing the attribute
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/node
cp /usr/bin/node /usr/bin/node.bak # WRONG: cp does not preserve xattrs by default
cp --preserve=all /usr/bin/node /usr/bin/node.bak # RIGHT: preserves capability xattr
Ein weiterer, oft übersehener Stolperstein: Capabilities werden als erweitertes Dateisystem-Attribut gespeichert und gehen bei einem einfachen cp ohne --preserve=all, bei einem Deployment über rsync ohne -X oder beim Neubau eines Docker-Images verloren, wenn die setcap-Zuweisung nicht Teil des Build-Prozesses ist. Wer Linux Capabilities in einer CI/CD-Pipeline nutzt, sollte den setcap-Schritt deshalb explizit in das Deployment-Skript aufnehmen, statt sich auf eine einmalig manuell gesetzte Capability zu verlassen.
9. Capabilities im Vergleich zu SUID, sudo und vollem Root
Es gibt mehrere etablierte Mechanismen, um einem Prozess erweiterte Rechte zu geben, die sich in Granularität und Risiko erheblich unterscheiden. Linux Capabilities sind dabei meist die präziseste und sicherste Option für Serverdienste.
| Mechanismus | Granularität | Risiko bei Kompromittierung | Typischer Einsatz |
|---|---|---|---|
| Linux Capabilities | Sehr fein (Bit-Ebene) | Gering, exakt begrenzt | Serverdienste, Container, systemd |
| SUID-Bit | Grob (voller Owner-Kontext) | Hoch, oft voller Root-Kontext | Legacy-Tools wie passwd, ping |
| sudo | Mittel (Befehlsebene) | Abhängig von sudoers-Regeln | Interaktive Administration |
| Volles Root | Keine | Maximal | Nur wenn wirklich unvermeidbar |
| SELinux/AppArmor (ergänzend) | Sehr fein (Regelbasiert) | Gering, aber komplexer zu pflegen | Zusätzliche Härtung neben Capabilities |
In der Praxis schließen sich diese Mechanismen nicht gegenseitig aus. Ein gut gehärteter Server kombiniert Linux Capabilities für präzise Prozessrechte, sudo für kontrollierte interaktive Administration und optional SELinux oder AppArmor als zusätzliche Schicht, die selbst innerhalb einer gewährten Capability noch feiner einschränkt, welche Dateien oder Netzwerkziele erreichbar sind.
Mironsoft
Server-Hardening, Least-Privilege-Design und Container-Sicherheit
Läuft bei euch noch alles als Root?
Wir analysieren eure Serverdienste und Container, ersetzen unnötige Root- und SUID-Rechte durch präzise vergebene Linux Capabilities und härten systemd-Services mit CapabilityBoundingSet.
Capability-Audit
Bestehende Root- und SUID-Nutzung analysieren und dokumentieren
Least-Privilege-Umbau
setcap, systemd-Direktiven und Container-Cap-Drop gezielt einsetzen
CI/CD-Integration
Capability-Zuweisung reproduzierbar in Deployment-Pipelines verankern
10. Zusammenfassung
Linux Capabilities ersetzen das grobe Root-oder-nichts-Modell durch über vierzig einzeln vergebbare Rechte, sodass ein Prozess genau die privilegierte Operation ausführen kann, die er braucht, ohne den Rest von Root zu erben. setcap weist diese Rechte dauerhaft einer Binary zu, capsh und /proc/PID/status machen sichtbar, welche Capabilities ein Prozess tatsächlich besitzt, und systemd-Direktiven wie AmbientCapabilities und CapabilityBoundingSet integrieren dieses Modell direkt in eigene Services.
Der größte Hebel liegt darin, Linux Capabilities konsequent statt SUID-Bits oder pauschalem Root-Start einzusetzen, sowohl bei klassischen Serverdiensten als auch in Containern mit --cap-drop=ALL als Ausgangspunkt. Wer diese Disziplin durchhält, reduziert die Angriffsfläche jedes einzelnen Dienstes erheblich, ohne auf die eigentlich benötigte Funktionalität verzichten zu müssen.
Linux Capabilities — Das Wichtigste auf einen Blick
Prinzip
Root-Rechte werden in über vierzig einzeln vergebbare Bits aufgeteilt, statt alles oder nichts zu erlauben.
setcap
setcap 'cap_net_bind_service=+ep' binary vergibt genau eine Fähigkeit, ohne Root-Start.
systemd
CapabilityBoundingSet begrenzt hart, welche Capabilities ein Service je erlangen kann.
Container
--cap-drop=ALL plus gezieltes --cap-add minimiert die Angriffsfläche im Container.