Weiterführende Literatur

Weiterführende Literatur

Ich schreibe einen HTTP-Server-Daemon in C (dafür gibt es Gründe) und verwalte ihn mit der systemd-Unit-Datei.

Ich schreibe eine Anwendung neu, die vor 20 Jahren, also etwa 1995, entwickelt wurde. Und das verwendete System besteht aus chroot und dann setuid und dem Standardverfahren.

In meiner früheren Arbeit war es üblich, dass man niemals einen Prozess als Root ausführt. Man erstellt einen Benutzer/eine Gruppe dafür und führt ihn von dort aus aus. Natürlich hat das System einige Dinge als Root ausgeführt, aber wir konnten die gesamte Geschäftslogikverarbeitung durchführen, ohne Root zu sein.

Nun zum HTTP-Daemon: Ich kann ihn ohne Root ausführen, wenn ich innerhalb der Anwendung kein Chroot ausführe. Ist es also nicht sicherer, wenn die Anwendung niemals als Root ausgeführt wird?

Ist es nicht sicherer, es von Anfang an als MyDaemon-Benutzer auszuführen? Anstatt es mit Root zu starten, zu chrooten und dann mit Setuid auf MyDaemon-Benutzer umzustellen?

Antwort1

Es scheint, dass andere Ihren Punkt übersehen haben, der nicht die Gründe waren, warum man geänderte Roots verwenden sollte, was Sie natürlich offensichtlich bereits wissen, noch was Sie sonst tun können, um Dæmons Grenzen zu setzen, wenn Sie auch offensichtlich wissen, dass Sie unter der Schirmherrschaft nicht privilegierter Benutzerkonten laufen müssen; sondern warum man diese Dinge tutinnerhalb der Anwendung. Dafür gibt es tatsächlich ein ziemlich treffendes Beispiel.

Betrachten Sie das Design des httpdDæmon-Programms in Daniel J. Bernsteins Publicfile-Paket. Als erstes ändert es das Root-Verzeichnis in das Root-Verzeichnis, das es mit einem Befehlsargument verwenden soll, und gibt dann die Berechtigungen auf die nicht privilegierten Benutzer-IDs und Gruppen-IDs ab, die in zwei Umgebungsvariablen übergeben werden.

Dæmon-Verwaltungstools verfügen über spezielle Tools für Dinge wie das Ändern des Stammverzeichnisses und das Herunterfallen auf nicht privilegierte Benutzer- und Gruppen-IDs. Gerrit Papes runit hatchpst. Mein Nosh-Toolset hatchrootUndsetuidgid-fromenv. Laurent Bercots s6 hats6-chrootUnds6-setuidgid. Wayne Marshalls Perp hatruntoolUndrunuidUnd so weiter. Tatsächlich haben sie alle M. Bernsteins eigenes Daemontools-Toolset mitsetuidgidals Antezedens.

Man könnte meinen, man könnte die Funktionalität aus httpdsolchen dedizierten Tools extrahieren und verwenden. Dann, wie Sie sich vorstellen,NEINEin Teil des Serverprogramms wird jemals mit Superuser-Berechtigungen ausgeführt.

Das Problem besteht darin, dass man als direkte Folge davon deutlich mehr Arbeit in die Einrichtung der geänderten Root stecken muss, was zu neuen Problemen führt.

Mit Bernstein httpdin seiner jetzigen Form, dienurDateien und Verzeichnisse, die sich im Stammverzeichnisbaum befinden, sind diejenigen, die für die Welt veröffentlicht werden sollen. Es gibtnichts anderesüberhaupt im Baum. Außerdem gibt es keinen Grund fürbeliebigIn diesem Baum muss eine ausführbare Programm-Image-Datei vorhanden sein.

Aber verschieben Sie den Wechsel des Stammverzeichnisses in ein Chain-Loading-Programm (oder systemd), und plötzlich werden die Programm-Image-Datei für httpd, alle gemeinsam genutzten Bibliotheken, die es lädt, und alle speziellen Dateien in /etc, /run, und /dev, auf die der Programmlader oder die C-Laufzeitbibliothek während der Programminitialisierung zugreift (was Sie vielleicht ziemlich überraschend finden, wenn Sie truss/ straceein C- oder C++-Programm),Auchmuss im geänderten Stammverzeichnis vorhanden sein. Andernfalls httpdkann es nicht verkettet werden und wird nicht geladen/ausgeführt.

Denken Sie daran, dass dies ein HTTP(S)-Inhaltsserver ist. Er kann potenziell jede (von allen lesbare) Datei im geänderten Stammverzeichnis bereitstellen. Dazu gehören jetzt Dinge wie Ihre gemeinsam genutzten Bibliotheken, Ihr Programmlader und Kopien verschiedener Loader-/CRTL-Konfigurationsdateien für Ihr Betriebssystem. Und wenn der Inhaltsserver auf irgendeine (versehentliche) Weise Zugriff hat aufschreibenEin infizierter Server kann möglicherweise selbst Schreibzugriff auf das Programmabbild httpdoder sogar auf den Programmlader Ihres Systems erhalten. (Denken Sie daran, dass Sie nun zwei parallele Sätze von /usr, /lib, /etc, /run, und /devVerzeichnissen haben, die Sie sichern müssen.)

Nichts davon ist der Fall, wenn httpdsich die Root-Berechtigungen ändern und selbst gelöscht werden.

Sie verfügen also nicht mehr über eine kleine Menge privilegierten Codes, der relativ einfach zu prüfen ist und gleich zu Beginn des httpdProgramms mit Superuser-Berechtigungen ausgeführt wird, sondern über eine erheblich erweiterte Angriffsfläche für Dateien und Verzeichnisse innerhalb des geänderten Stammverzeichnisses.

Aus diesem Grund ist es nicht so einfach, alles außerhalb des Serviceprogramms zu erledigen.

Beachten Sie, dass dies dennoch nur ein Minimum an Funktionalität in httpdsich selbst ist. Der gesamte Code, der Dinge tut wie zum Beispiel in der Kontendatenbank des Betriebssystems nach der Benutzer-ID und der Gruppen-ID zu suchen, um sie in diese Umgebungsvariablen einzutragenIstaußerhalb des httpdProgramms, in einfachen, eigenständigen, überprüfbaren Befehlen wie envuidgid. (Und natürlich ist es ein UCSPI-Tool, also enthält es keinen Code zum Abhören der relevanten TCP-Ports oder zum Akzeptieren von Verbindungen, das ist die Domäne von Befehlen wie .tcpserver,tcp-socket-listen,tcp-socket-accept,s6-tcpserver4-socketbinder,s6-tcpserver4d, und so weiter.)

Weiterführende Literatur

Antwort2

Ich denke, viele Details Ihrer Frage könnten genauso auf zutreffen avahi-daemon, das ich mir kürzlich angesehen habe. (Vielleicht habe ich jedoch ein anderes Detail übersehen, das anders ist). Das Ausführen von Avahi-Daemon in einem Chroot hat viele Vorteile, falls Avahi-Daemon kompromittiert ist. Dazu gehören:

  1. Es kann keine Benutzer-Home-Verzeichnisse lesen und private Informationen exfiltrieren.
  2. Es kann keine Fehler in anderen Programmen ausnutzen, indem es in /tmp schreibt. Es gibt mindestens eine ganze Kategorie solcher Fehler. Z. B.https://www.google.co.uk/search?q=tmp+race+security+bug
  3. Es können keine Unix-Socket-Dateien außerhalb des Chroot-Bereichs geöffnet werden, in denen andere Daemons möglicherweise Nachrichten abhören und lesen.

Punkt 3 könnte besonders schön sein, wenn SienichtVerwendung von dbus oder ähnlichem ... Ich glaube, der Avahi-Daemon verwendet dbus, sodass er auch innerhalb des Chroots Zugriff auf den System-dbus hat. Wenn Sie die Möglichkeit, Nachrichten über den System-dbus zu senden, nicht benötigen, kann das Verweigern dieser Möglichkeit eine ganz nette Sicherheitsfunktion sein.

Verwalten mit der Systemd-Unit-Datei

Beachten Sie, dass sich der Avahi-Daemon bei einer Neufassung möglicherweise auf systemd verlassen und eg verwenden könnte ProtectHome. Ich habe eine Änderung am Avahi-Daemon vorgeschlagen, um diese Schutzmechanismen als zusätzliche Ebene hinzuzufügen, zusammen mit einigen zusätzlichen Schutzmechanismen, die von chroot nicht garantiert werden. Die vollständige Liste der von mir vorgeschlagenen Optionen finden Sie hier:

https://github.com/lathiat/avahi/pull/181/commits/67a7b10049c58d6afeebdc64ffd2023c5a93d49a

Es sieht so aus, als ob es mehr Einschränkungen gibt, die ich hätte nutzen können, wenn avahi-daemonnichtVerwenden Sie Chroot selbst. Einige davon werden in der Commit-Nachricht erwähnt. Ich bin mir jedoch nicht sicher, inwieweit dies zutrifft.

Beachten Sie, dass die von mir verwendeten Schutzmaßnahmen den Daemon nicht daran gehindert hätten, Unix-Socket-Dateien zu öffnen (Punkt 3 oben).

Ein anderer Ansatz wäre die Verwendung von SELinux. Allerdings würden Sie Ihre Anwendung damit an diese Teilmenge von Linux-Distributionen binden. Der Grund, warum ich SELinux hier positiv fand, ist, dass SELinux den Zugriff von Prozessen auf dbus auf feinkörnige Weise einschränkt. Ich denke beispielsweise, dass Sie oft erwarten würden, dass dies systemdnicht in der Liste der Busnamen enthalten wäre, an die Sie Nachrichten senden müssen :-).

„Ich habe mich gefragt, ob die Verwendung von systemd-Sandboxing sicherer ist als chroot/setuid/umask/...“

Zusammenfassung: Warum nicht beides? Lassen Sie uns das oben Gesagte ein wenig entschlüsseln :-).

Wenn Sie über Punkt 3 nachdenken, bietet die Verwendung von chroot mehr Beschränkungen. ProtectHome= und seine Freunde versuchen nicht einmal, so restriktiv zu sein wie chroot. (Beispielsweise führt keine der benannten systemd-Optionen eine Blacklist durch /run, wo wir normalerweise Unix-Socket-Dateien ablegen).

chroot zeigt, dass die Einschränkung des Dateisystemzugriffs eine sehr mächtige, aber nichtallesunter Linux ist es eine Datei :-). Es gibt systemd-Optionen, die andere Dinge einschränken können, die keine Dateien sind. Dies ist nützlich, wenn das Programm kompromittiert ist, denn Sie können die ihm zur Verfügung stehenden Kernelfunktionen einschränken, in denen es möglicherweise versucht, eine Schwachstelle auszunutzen. Beispielsweise benötigt der Avahi-Daemon keine Bluetooth-Sockets und ich vermute, Ihr Webserver auch nicht :-). Geben Sie ihm also keinen Zugriff auf die AF_BLUETOOTH-Adressfamilie. Setzen Sie einfach AF_INET, AF_INET6 und vielleicht AF_UNIX mithilfe der RestrictAddressFamilies=Option auf die Whitelist.

Bitte lesen Sie die Dokumentation für jede Option, die Sie verwenden. Einige Optionen sind in Kombination mit anderen effektiver und einige sind nicht auf allen CPU-Architekturen verfügbar. (Nicht, weil die CPU schlecht ist, sondern weil der Linux-Port für diese CPU nicht so gut gestaltet war. Denke ich).

(Hier gilt ein allgemeines Prinzip. Es ist sicherer, wenn Sie Listen mit den Dingen schreiben können, die Sie zulassen möchten, und nicht mit den Dingen, die Sie verbieten möchten. So erhalten Sie beispielsweise durch die Definition einer Chroot-Umgebung eine Liste mit den Dateien, auf die Sie zugreifen dürfen, und das ist zuverlässiger, als wenn Sie angeben, dass Sie etwas blockieren möchten /home.)

Im Prinzip könnten Sie dieselben Einschränkungen auch selbst vor setuid() anwenden. Es handelt sich lediglich um Code, den Sie von systemd kopieren könnten. Systemd-Unit-Optionen sollten jedoch wesentlich einfacher zu schreiben sein, und da sie in einem Standardformat vorliegen, sollten sie leichter zu lesen und zu überprüfen sein.

Ich kann Ihnen also nur wärmstens empfehlen, den Abschnitt über Sandboxing man systemd.execauf Ihrer Zielplattform durchzulesen. Wenn Sie jedoch das sicherste Design wünschen, das möglich ist, würde ich keine Angst haben, es in Ihrem Programm zu versuchen chroot(und dann rootdie Berechtigungen zu löschen).sowie. Hier gibt es einen Kompromiss. Die Verwendung von chrootbringt einige Einschränkungen für Ihr Gesamtdesign mit sich. Wenn Sie bereits ein Design haben, das chroot verwendet, und es das zu tun scheint, was Sie brauchen, klingt das ziemlich gut.

Antwort3

Wenn Sie sich auf systemd verlassen können, ist es tatsächlich sicherer (und einfacher!), die Sandbox-Funktion systemd zu überlassen. (Natürlich kann die Anwendung auch erkennen, ob sie von systemd in einer Sandbox gestartet wurde oder nicht, und selbst eine Sandbox ausführen, wenn sie noch Root ist.) Das Äquivalent des von Ihnen beschriebenen Dienstes wäre:

[Service]
ExecStart=/usr/local/bin/mydaemon
User=mydaemon-user
RootDirectory=...

Aber das ist noch nicht alles. systemd kann auch viele andere Sandboxing-Aufgaben für Sie übernehmen – hier sind einige Beispiele:

[Service]
# allocate separate /tmp and /var/tmp for the service
PrivateTmp=yes
# mount / (except for some subdirectories) read-only
ProtectSystem=strict
# empty /home, /root
ProtectHome=yes
# disable setuid and other privilege escalation mechanisms
NoNewPrivileges=yes
# separate network namespace with only loopback device
PrivateNetwork=yes
# only unix domain sockets (no inet, inet6, netlink, …)
RestrictAddressFamilies=AF_UNIX

Unter finden Sie man 5 systemd.execviele weitere Anweisungen und ausführlichere Beschreibungen. Wenn Sie Ihren Daemon socket-aktivierbar machen ( man 5 systemd.socket), können Sie sogar die netzwerkbezogenen Optionen verwenden: Die einzige Verbindung des Dienstes zur Außenwelt ist der Netzwerk-Socket, den er von systemd erhalten hat, er kann keine Verbindung zu anderen Servern herstellen. Wenn es sich um einen einfachen Server handelt, der nur auf einigen Ports lauscht und keine Verbindung zu anderen Servern herstellen muss, kann dies nützlich sein. (Die dateisystembezogenen Optionen können RootDirectorymeiner Meinung nach auch die obsolet machen, sodass Sie sich vielleicht nicht mehr die Mühe machen müssen, ein neues Stammverzeichnis mit allen erforderlichen Binärdateien und Bibliotheken einzurichten.)

Neuere systemd-Versionen (seit v232) unterstützen auch DynamicUser=yes, wobei systemd Ihnen den Dienstbenutzer automatisch nur für die Laufzeit des Dienstes zuweist. Das bedeutet, dass Sie keinen permanenten Benutzer für den Dienst registrieren müssen, und funktioniert einwandfrei, solange der Dienst nicht an andere Dateisystemspeicherorte als seine StateDirectory, LogsDirectory, und schreibt CacheDirectory(die Sie auch in der Unit-Datei deklarieren können – siehe man 5 systemd.execerneut – und die systemd dann verwaltet und darauf achtet, sie dem dynamischen Benutzer korrekt zuzuweisen).

verwandte Informationen