Lange Laufzeit beweist nur, dass der Start mit den Privilegien gelang, die der Daemon in diesem Moment hatte. Die Fehler, auf die es hier ankommt, kommen später: ein neuer Client verbindet sich, eine Sperrliste wird ersetzt, ein Hilfsskript muss eine Datei schreiben. Jeder davon ist ein neues Öffnen durch das unprivilegierte Konto, mit eigener Rechtefrage und eigener Fehlermeldung. Drei Entscheidungen trennen eine Konfiguration, die weiterläuft, von einer, die beim nächsten Ereignis scheitert.

Trennen Sie früh Geladenes von später Geöffnetem

Ein Deskriptor ist ein Handle auf eine offene Dateibeschreibung, und der Zugriffsmodus wurde bei Erstellung dieser Beschreibung festgelegt. Ein späterer Wechsel des Prozessbenutzers schließt das Handle nicht, deshalb funktioniert eine als root gelesene Schlüsseldatei nach dem Abstieg weiter. Auch der Modus ändert sich nicht: Ein zum Lesen geöffnetes Handle wird nicht schreibbar, und ein schreibbares Handle wird nicht entzogen, nur weil das Konto seine Rechte am Pfad verloren hat. Diese zwei Tatsachen erklären den größten Teil des überraschenden Verhaltens in diesen Berichten.

Die andere Hälfte ist Speicher. Ein Daemon kann ein Zertifikat parsen, das geparste Material behalten und die Datei schließen; in Benutzung bleiben Daten, keine offene Datei. Fortlaufender Verkehr beweist daher weder, dass ein Deskriptor offen ist, noch dass der Pfadname erreichbar ist. Er beweist, dass das für die laufende Sitzung nötige Material früher beschafft wurde.

Ein späteres Öffnen ist eine andere Transaktion. Es läuft unter den aktuellen Anmeldedaten, dem aktuellen Sicherheitskontext und der aktuellen Einhängesicht, sodass ein beim Start lesbarer Pfad bei der nächsten Verbindung abgelehnt werden kann. Rotation fügt eine Falle hinzu: Eine neue Datei über denselben Pfadnamen zu schreiben lässt einen bestehenden Deskriptor am alten Objekt hängen, sodass ein Neuladen erfolgreich erscheinen kann, während der Daemon noch den vorherigen Inhalt nutzt. Vergleichen Sie die Inode des Prozesses mit der auf der Platte, bevor Sie einer Rotation glauben.

Benennen Sie die drei Laufzeit-Lesezugriffe und wann jeder ausgelöst wird

Drei Ressourcen werden normalerweise lange nach der Initialisierung geöffnet, und sie scheitern zu unterschiedlichen Zeitpunkten. Eine client-spezifische Konfigurationsdatei wird gelesen, wenn genau dieser Client sich verbindet, kann also für einen Nutzer brechen, während alle anderen weiterlaufen. Sperrdaten werden bei der Peer-Authentifizierung und erneut beim Ersetzen der Datei konsultiert. Ein temporäres Verzeichnis wird nur berührt, wenn ein Skript oder Plugin etwas schreibt.

Keine der drei gehört zum Startpfad, deshalb kann ein wochenlang gesunder Server bei der nächsten Verbindung scheitern. Die Kategorie benennt auch den Eigentümer der Korrektur: Die ersten beiden sind Leserechte auf Konfiguration unter dem Daemon-Konto, die dritte ist ein beschreibbarer Ort mit engem Zweck.

Path reopened laterWhat triggers the openFirst sign when it fails
Client configuration fileA connection for that client identityOne client rejected while others connect
Revocation dataPeer authentication, including after replacementRevoked and valid peers both refused
Script or plugin temporary directoryA helper needs to create a fileThe helper exits with a path error

persist-key beantwortet eine engere Frage, als die meisten annehmen. Es hält die Schlüsseldatei über die dokumentierten SIGUSR1- und ping-restart-Resets hinweg offen, und es tut nichts für CCD-Dateien, Sperrdaten, Helferprogramme oder einen vollständigen Prozessneustart. Diese folgen weiter ihren eigenen Öffnungspfaden, unter welchem Konto auch immer gerade läuft.

Bestätigen Sie das Konto und die Sicht, die es tatsächlich hat

Fragen Sie den laufenden Daemon, welches Konto er nutzt, statt der Unit-Datei zu vertrauen, und vergleichen Sie die Antwort mit dem, was die Unit setzt. Eine Abweichung ist zuerst zu klären, denn jede spätere Rechteprüfung läuft als derjenige, der der Prozess wirklich ist. Gehen Sie dann den Pfad Komponente für Komponente; Durchquerung wird auf jeder Ebene bewertet, eine lesbare Datei in einem nicht durchquerbaren Verzeichnis bleibt also unlesbar. Der endgültige Dateimodus ist die letzte Frage, nicht die erste.

Namespaces und Dienst-Sandboxes fügen Grenzen hinzu, die Eigentum und Modusbits nicht beschreiben. Ein in Ihrer Shell sichtbarer Pfad kann in einem chroot, einem Mount-Namespace oder einer Unit, die Home-Verzeichnisse verbirgt, fehlen oder nur lesbar sein. Sicherheitsmodule fügen eine weitere Schicht hinzu, sodass eine scheinbar lesbare Datei beim Öffnen dennoch verweigert werden kann.

ps -o user:12,pid,args -C openvpn
systemctl show -p User -p Group -p ProtectHome -p RootDirectory [email protected]
sudo -u openvpn namei -l /etc/openvpn/ccd/alice

Ein erfolgreiches Lesen in der Shell des Administrators prüft keine dieser kombinierten Bedingungen. Führen Sie die Prüfung unter dem Daemon-Konto aus, mit demselben Namespace und derselben Sandbox, oder akzeptieren Sie, dass das Ergebnis Ihre Shell beschreibt und nicht den Server. Der dritte Befehl ist die billige Variante dieses Tests: Er druckt den Modus jeder Komponente und scheitert an der ersten, die das Daemon-Konto nicht durchqueren kann.

Geben Sie dem Laufzeitschreiben ein eigenes Verzeichnis

Lesen ist nur die Hälfte der Bestandsaufnahme. Ein Hilfsskript muss vielleicht eine Statusdatei anlegen, ein Plugin Zustand neben seiner Konfiguration schreiben, und diese Schreibvorgänge sind nicht dadurch abgedeckt, dass der Konfigurationsbaum beschreibbar wird. Geben Sie dem konkreten Verzeichnis die Schreibberechtigung, die seine Funktion braucht, mit einem Eigentümer und Modus, den Sie erklären können. Bevorzugen Sie Nur-Lese-Zugriff für alles, was nur gelesen wird.

Halten Sie die Rechte des privaten Schlüssels getrennt von den Richtliniendateien. Ein Schlüssel, der nach einem Reset erneut gelesen werden muss, hat eine andere Anforderung als eine Client-Datei, die bei jeder Verbindung gelesen wird, und ein großzügiger Modus für beide gibt mehr Zugriff, als das Problem verlangt. Ein eigenes Daemon-Konto mit engem Eigentum ist leichter zu prüfen als eine breite Gruppe. Wenn der Schlüssel Resets überleben muss, persist-key beseitigt es das erneute Lesen, statt das Verzeichnis zu weiten.

Entscheiden Sie nach dem Ereignis, das Sie reproduzieren können

Ein laufender Tunnel beweist einen Pfad durch den Code. Das Ereignis, das die Datei braucht, ist der einzige Test, der das Öffnen abdeckt, das Sie interessiert, sei es eine neue Client-Verbindung, eine ersetzte Sperrdatei, ein erlaubter Soft-Reset oder ein geplanter Neustart. Wählen Sie das Ereignis, führen Sie es aus und behalten Sie den ersten Fehler statt des letzten.

Bei einem Soft-Reset zeigen ein Signal und das Journal der Unit, ob der Schlüssel erneut gelesen wurde. Senden Sie das Signal und lesen Sie dann die letzten Zeilen der Unit:

systemctl kill -s SIGUSR1 [email protected]
journalctl -u [email protected] -n 40 --no-pager

Schließen Sie nicht daraus, dass ein kompromittierter Daemon keine Dateien mehr ändern kann, nur weil Privilegien abgesenkt wurden. Was er noch kann, hängt von verbleibenden Anmeldedaten, seinen Fähigkeiten, den beschreibbaren Verzeichnissen und jedem noch gehaltenen schreibbaren Deskriptor ab. Privilegienreduktion verengt die Autorität, und die Bestandsaufnahme der Laufzeit-Öffnungen zeigt, wie weit. Schreiben Sie diese Grenze neben die Konfiguration, damit die nächste Person sie nicht neu entdecken muss.

Ähnliche Artikel