chroot scheitert unter OpenVPN: AppArmor-Regel oder Capability?
Ein Pfad in einem AppArmor-Profil verleiht nie CAP_SYS_CHROOT, und ein verweigertes chroot kann aus drei getrennten Schichten kommen. Finden Sie heraus, welche zu ändern ist.
Der Daemon protokolliert einen chroot-Fehler, und das Profil sieht ohnehin großzügig aus, also ist die naheliegende Reparatur, eine Pfadregel zu erweitern und neu zu starten. Das ändert meist nichts, weil chroot durch ein Privileg geschützt wird, das das Profil nie übergeben konnte. Welche Schicht zu berühren ist, entscheiden drei Prüfungen, und sie sind billig: was der Prozess halten darf, was die Richtlinie erlaubt und was der Starter entfernt hat, bevor eines der beiden zählte.
Zwei getrennte Tore, und nur eines gehört AppArmor
chroot braucht CAP_SYS_CHROOT in jenem Benutzer-Namensraum, in dem der Prozess läuft. Das ist ein gewöhnliches Linux-Privileg, und der Kernel prüft es, bevor der Aufruf irgendetwas mit Pfaden tut. Davon getrennt kann ein durchsetzendes AppArmor-Profil die Capability auch dann verweigern, wenn der Prozess sie hält, weil zwingende Richtlinien zusätzlich zu den Credentials herangezogen werden und nicht an deren Stelle. Der Unterschied ist praktisch wichtig: die beiden Schichten liegen in verschiedenen Dateien und werden meist von verschiedenen Personen gepflegt.
Beide Tore scheitern mit demselben errno. Beide ergeben EPERM, und beide erscheinen im Daemon-Log als allgemeine Berechtigungsbeschwerde, weshalb Raten über den Profilinhalt so viel Zeit kostet. Die Pfadregel, die Sie gerade hinzufügen wollten, regelt den Dateizugriff, nachdem chroot gelungen ist; sie hat keinen Einfluss darauf, ob die Operation überhaupt erlaubt ist.
Ein drittes Tor liegt oberhalb beider. Hat der Dienstmanager oder ein Wrapper den Prozess ohne die Capability in seinem Bounding-Set gestartet, kann keine AppArmor-Änderung sie zurückholen, und Capabilities im Profil zu ergänzen dokumentiert nur eine Absicht, die der Kernel nicht ehren wird.
Lesen Sie den Privilegstatus, bevor Sie die Richtlinie bearbeiten
Gehen Sie vom laufenden Prozess aus und nicht von der Konfiguration, die Sie für aktiv halten. Die Statusdatei zeigt fünf Capability-Masken und das Flag für keine neuen Privilegien, und sie beantworten verschiedene Fragen:
grep -E '^(CapInh|CapPrm|CapEff|CapBnd|CapAmb|NoNewPrivs):' /proc/$(pidof openvpn)/status
sudo aa-status | grep -i openvpnCapEff ist die Menge, auf die es beim Aufruf ankommt: eine Capability, die in CapBnd steht, aber in CapEff fehlt, ist durch die Obergrenze erlaubt und wird derzeit nicht gehalten. CapAmb zählt, wenn die Binärdatei nicht im üblichen setuid-Sinn privilegiert ist, denn ambiente Capabilities sind der Weg, auf dem ein Dienst ein Privileg über exec hinweg behält, ohne Datei-Capabilities. CapInh enthält Bits, die über exec vererbbar wären, aber nur wenn die vererbbare Menge der ausgeführten Datei es zulässt, ein Bit dort allein nützt also nichts.
Bestätigen Sie dann, welches Profil tatsächlich angehängt ist. Ein Prozess kann durch einen Profilnamen eingeschränkt sein, den Sie nicht erwartet haben, besonders wenn eine Distribution ihre Unit-Datei und ihr Profil zusammen ausliefert, und das Lesen von /proc/PID/attr/current klärt das in einer Zeile. Ihre bearbeitete Datei mit dem Profil zu vergleichen, das der Kernel meldet, ist der Unterschied zwischen die Ursache beheben und eine Datei bearbeiten, die niemand lädt.
Ergänzen Sie die Capability-Regel nur, wenn die Prüfspur es sagt
Kernel- und Audit-Einträge benennen die verweigernde Schicht. Suchen Sie eine Ablehnung, die die Operation capable und den Namen sys_chroot trägt, zusammen mit dem Profil, das sie erzeugt hat. Existiert dieser Eintrag, hielt der Prozess die Capability bereits und AppArmor hat sie verweigert, und die gezielte Änderung ist eine Capability-Regel im echten Profil:
capability sys_chroot,Halten Sie sie als Fragment in dem Profil, das wirklich angehängt ist, und prüfen Sie das Schlüsselwort gegen die Referenz zu apparmor.d-Profilen, statt eine Regel aus einem unpassenden Tutorial zu kopieren. In einem geteilten Include gibt sie jedem eingeschränkten Programm die Capability, das diese Datei einbindet, und Profile in den Complain-Modus zu schalten verdeckt die nächste Ablehnung, statt diese zu lösen. Die Profilregel nimmt eine Richtlinienablehnung weg; sie erzeugt kein Privileg, ein Prozess ohne CAP_SYS_CHROOT in seiner effektiven Menge scheitert also auch mit der Regel.
Zeigt der Audit-Eintrag überhaupt keine AppArmor-Ablehnung, hören Sie auf, das Profil zu bearbeiten. Sie sehen ein Credential-Problem, und die restliche Arbeit liegt in der Unit-Datei oder im Elternprozess, nicht in der Richtliniensprache.
Der Dienstmanager hat seine eigene Obergrenze
systemd setzt für jeden Dienst ein Capability-Bounding-Set, und standardmäßig ist dieses Set eng. Was dort ausgeschlossen ist, steht dem Prozess nicht zur Verfügung, egal was das Profil sagt, weshalb gehärtete Units an einer Operation scheitern können, die lief, als der Daemon von Hand gestartet wurde. Die relevanten Direktiven sind in der Unit leicht zu lesen und leicht zu übersehen, wenn die Anwendung den Fehler meldet. Drop-ins aus einem Distributionspaket sind die übliche Quelle einer Obergrenze, von der niemand weiß, dass er sie konfiguriert hat.
Die Obergrenze anheben und die Capability gewähren sind zwei Änderungen, nicht eine. Ein Eintrag im Bounding-Set sagt, dass der Prozess die Capability halten darf; er fügt sie nicht der effektiven Menge hinzu, und NoNewPrivs kann eine spätere Erhöhung verhindern, selbst wenn sonst alles passt. Ergänzen Sie nur, was die Operation braucht, lassen Sie die übrige Härtung intakt und widerstehen Sie der Abkürzung, alle Capabilities zu geben, damit ein Aufruf durchgeht.
Auch die Reihenfolge zählt hier. Ändern Sie zuerst die Obergrenze der Unit, prüfen Sie, dass CapEff das Bit nun enthält, und entscheiden Sie erst dann, ob AppArmor weiterhin verweigert. Beides gleichzeitig zu ändern lässt Sie bei anhaltendem Fehler wieder raten.
Wenn die Ablehnung weg ist, prüfen Sie den Käfig selbst
Ein erfolgreiches chroot verschiebt die Wurzel, also wird jeder Pfad, den der Daemon danach berührt, innerhalb des neuen Baums aufgelöst. Fehlende Konfiguration, ein fehlender Geräteknoten oder ein Zertifikatsverzeichnis, das nicht kopiert wurde, erscheinen nun als Dateifehler, und dieser Fehler ist ein Layoutproblem, kein Capability-Problem. Lesen Sie das nächste Ereignis, statt eine weitere breite Berechtigung zu ergänzen. Das Verschieben ändert auch, was ein relativer Pfad bedeutet, weshalb Härtungsleitfäden auf absoluten Pfaden in der Daemon-Konfiguration bestehen.
Es hilft auch, sich zu merken, was chroot nicht tut. Es ist eine Pfadgrenze, keine Sandbox: ein Prozess, der das Privileg behält, kann aus ihr ausbrechen, weshalb die Capability nach dem Aufruf meist fallengelassen wird und weshalb die Einschränkung um den Daemon herum bestehen bleiben sollte. AppArmor-Profil, Capability-Bounding-Set und Dateisystembeschränkungen zusammen zu behalten ist der Sinn der Übung, kein Überbleibsel, das man entfernt, sobald der Dienst startet.
Die Entscheidungsregel ist kurz. Nennt der Audit-Eintrag AppArmor, ergänzen Sie eine Capability-Regel im angehängten Profil. Ist die effektive Menge leer, korrigieren Sie die Obergrenze der Unit und die Reihenfolge des Privilegienverlusts. Ist das Profil nicht das, das Sie bearbeitet haben, hören Sie auf zu bearbeiten und finden Sie das echte.