Guards, und warum v1 nicht durchsetzt
Eine Härtungsrichtlinie kann Sie aus der Maschine aussperren, die Sie gerade härten.
bitenforcer misst dieses Risiko an der Sitzung, in der Sie tatsächlich arbeiten, und meldet
es, bevor irgendetwas angewendet wird. Dieselben Guards sind der Grund, warum v1 überhaupt
nichts anwendet: Sieben unabhängige adversariale Prüfer kamen jeweils mit einem
Aussperr-Muster an, das sie nicht erkannten.
Diese Seite behandelt die Guards, wie Sie eine Richtlinie in CI daran absichern, und die vollständige Darlegung, warum die Durchsetzung außerhalb des Funktionsumfangs dieses Releases liegt. Was das Werkzeug heute tut, steht im bitenforcer-Überblick.
Die Lockout-Guards
Achtzehn Guards, jeder für eine Art, wie eine Härtungsrichtlinie Sie aus Ihrer eigenen
Maschine aussperren kann. Auflisten mit bitenforcer guards:
ssh-operator-lockout the SSH change would lock out the account you are running as
(FR) la modification SSH verrouillerait le compte avec lequel
vous êtes connecté
--acknowledge ssh-operator-lockout
firewall-ipv6-unhandled IPv4 would be locked down while IPv6 stays open (silent bypass)
(FR) IPv4 serait verrouillé alors qu'IPv6 resterait ouvert
--acknowledge firewall-ipv6-unhandled
Vollständige Liste: session-undetermined, ssh-no-auth-method, ssh-operator-lockout,
ssh-listen-address-unusable, ssh-listen-address-excludes-session,
ssh-crypto-narrowing, firewall-session-drop, firewall-deny-before-allow,
firewall-ipv6-unhandled, selinux-relabel-required, selinux-port-context-loss,
selinux-access-loss, services-remote-access-loss, users-operator-lockout,
kernel-remote-access-loss, kernel-boot-risk, security-regression,
unreviewed-change.
Zwei Design-Regeln sind wichtig:
--forceEin Guard wird nur durch namentliche Nennung außer Kraft gesetzt —
--acknowledge <guard-id> — und einen Guard zu quittieren lässt jeden anderen scharf.
unreviewed-change lässt sich überhaupt nicht als Klasse quittieren: Das Token benennt genau
ein Modul, eine Art und einen Schlüssel, und die Ablehnung selbst gibt das exakte zu
verwendende Token aus.
Eine Sitzung, die nicht gemessen werden konnte, ist nicht quittierbar — für jeden Lauf, der den Fernzugriff berührt. Wer als Operator nicht lokalisiert werden kann, kann nicht geschützt werden, und etwas anderes vorzugeben ist genau der Weg, auf dem Menschen sich aussperren.
Eine Richtlinie in CI absichern
--guards=blocking verwandelt jede nicht quittierte Feststellung in einen Exit-Code ungleich
null:
bitenforcer apply --config policy.yaml --dry-run --guards=blocking
# exit 1 when any guard fires — fails the pipeline
So verhindern Sie, dass eine Härtungs-Baseline, die den Fernzugriff kappen würde, überhaupt jemals ein Änderungsfenster erreicht.
Warum die Durchsetzung nicht in v1 ist
Der Durchsetzungspfad wurde gebaut. Er wurde anschließend siebenmal adversarial geprüft,
von sieben unabhängigen Prüfern auf echten Hosts. Jeder Einzelne von ihnen hat sich
entweder selbst aus dem Testhost ausgesperrt oder die Bestätigung gefälscht, die genau das
hätte abfangen sollen — zehn verschiedene Aussperr-Muster, mehrere davon meldeten dabei
Erfolg und Exit-Code 0.
Die Muster waren nie zweimal dieselben: sshd_config-Bytes; ein Firewall-Plan; ein
systemd-Härtungs-Drop-in, das kein „stop“ ist und deshalb den Evaluator nie erreichte; eine
Eigentümer-/Gruppenänderung an authorized_keys; ein sysctl außerhalb der beiden Muster, die
ein Evaluator erkannte; eine „überlebende“ Authentifizierungsmethode, die das nur mit
Schlüssel ausgestattete Konto des Operators gar nicht nutzen konnte; eine über Loopback
akzeptierte Bestätigung; eine Prüfung, die nicht durchgeführt werden konnte und als
Fehlschlag gelesen wurde.
Jede Welle schloss das Muster, das ihr gezeigt worden war, und der nächste Prüfer kam mit einem neuen an. Das ist die Signatur eines Open-World-Problems: „zähle jede Möglichkeit auf, wie eine Richtlinie diesen Host unerreichbar machen könnte“ hat kein letztes Element, und jeder Build der Guards ist eine Liste der Angriffe, die schon jemand bedacht hat.
Ein Totmann-Rollback wurde genau deshalb gebaut, weil es keine Aufzählung braucht — was auch immer den Host kaputt gemacht hat, die Bestätigung trifft nicht ein und die Änderung wird zurückgenommen. Es ist das richtige Modell, und der Code bleibt erhalten. Auch er hat die Überprüfung nicht überstanden, und seine letzte Feststellung beendete die Diskussion:
Die Wiederherstellungsmaschinerie wurde selbst zu einer lokalen Root-Rechteausweitung. Das Totmann-Rollback stellte eine Berechtigung über einen Symlink wieder her, den eine unprivilegierte Benutzerin in ihrem eigenen
~/.sshplatziert hatte, und brachte/etc/shadowvon0640auf0777. Das Manifest des Laufs vermerkte „4 restored, 0 failed“.
Das auszuliefern hätte auf jeder Kundenmaschine eine Root-Rechteausweitung installiert, geliefert vom Sicherheitsnetz. Deshalb liegt die Durchsetzung außerhalb des Funktionsumfangs von v1.
Was bewiesen wurde, und wie
Ein Fingerabdruck des gesamten Dateisystems — Pfad, Modus, Eigentümer, Größe und SHA-256
jeder Datei unter /etc, /var/lib und den systemd-Bäumen —, erhoben vor und nach einem
Lauf aller Module als Root, in einem Container mit echtem sshd, nft, iptables und
getenforce, ist identisch. Sowohl für apply (das sich verweigert) als auch für
apply --dry-run (das berichtet). Das sind 39 Zusicherungen im End-to-End-Oberflächentest
des Repositories.
Zwei Builds wurden anschließend absichtlich falsch gemacht und durch dasselbe Skript geschickt, denn ein Test, den niemand zu brechen versucht hat, ist Dekoration:
- Scope-Statement gelöscht → 6 Fehlschläge, darunter
ROOT apply CHANGED the filesystem; - der zurückgehaltene
deadman-Befehl wieder registriert → 7 Fehlschläge, jeder benennt den Befehl, der zurückkam, und den Zustand, den er erzeugt hat.
Was v1 bewusst nicht ausliefert
| Befehl | In v1 | Warum |
|---|---|---|
validate | ✅ | Weist eine ungültige Richtlinie zurück — und weist Schlüssel zurück, die nichts implementiert. |
apply --dry-run | ✅ | Der Pre-Flight. |
apply --dry-run --guards=blocking | ✅ | Richtlinien-Gate für CI. |
guards, session, version | ✅ | Rein lesend. |
generate | ✅ | Rein lesend außer mit --output-file, das schreibt — siehe die Warnung unten. |
apply (durchsetzend) | ❌ | Nennt den Funktionsumfang des Releases in EN + FR, benennt, was Sie stattdessen ausführen können, und endet mit Exit-Code ungleich null. |
rollback, confirm | ❌ | Nichts in v1 kann einen Snapshot erzeugen oder ein Rollback scharfstellen, sodass beide immer nur antworten könnten „hier ist nichts“ — was trotzdem eine Fähigkeit bewirbt. |
deadman | ❌ | Zurückgehalten. Wiederherstellen ist ein Ändern des Hosts, über ein vom Operator angegebenes Zustandsverzeichnis — genau hier saß die Rechteausweitung. |
--output-file schreibt auf den Host und überschreibt eine vorhandene Datei--output-file ist ein globales Flag, gilt also auch für generate. Ist es gesetzt, legt
generate das übergeordnete Verzeichnis an (0750) und schreibt die Datei (0640) — ohne
Existenzprüfung, ohne O_EXCL und ohne Rückfrage. Zeigt es auf einen Pfad, der bereits
existiert, ersetzt es diese Datei.
Diese Seite führte generate zuvor unter den Befehlen auf, die „alle rein lesend“ seien, und
genau darin lag die Gefahr: Im Vertrauen auf diesen Satz richtete ein Operator --output-file
auf einen Pfad unter /etc, und der Befehl überschrieb die dort bereits vorhandene
Konfiguration. Alles Übrige in der Tabelle ist tatsächlich rein lesend; dieses eine Flag ist
die Ausnahme, und sie wird hier benannt statt stillschweigend korrigiert.
apply ohne --dry-run auszuführen ist kein stiller No-Op. Es sagt Ihnen die Wahrheit und
endet mit einem Exit-Code ungleich null:
$ bitenforcer apply --config policy.yaml
Error: apply: this release does not change hosts, and nothing on this one was changed.
bitenforcer v1 ships policy validation and pre-flight. Enforcement — writing
sshd_config, firewall rules, sysctls, accounts and unit state — is deliberately not
part of it, and no flag turns it on. It is held back because seven independent
adversarial reviews of that path each locked themselves out of a test host or forged
the confirmation meant to catch it.
What this build does, and does well:
bitenforcer apply --config policy.yaml --dry-run
bitenforcer apply --config policy.yaml --dry-run --guards=blocking
bitenforcer validate --config policy.yaml
(FR) Cette version ne modifie aucun hôte et rien n'a été modifié ici. […]
Nichts wird aus der Codebasis gelöscht. Die Engine, die Guards, der Snapshot-/Rollback-Code und die Totmann-Maschinerie bleiben allesamt erhalten, mit ihren Tests. Sie sind das Fundament des nächsten Releases. Sie sind schlicht an nichts angeschlossen, das Ihren Host ändern kann.
Wann die Durchsetzung zurückkommt
Die Messlatte ist aufgeschrieben, damit sie später nicht heruntergehandelt werden kann:
Ein unabhängiger Prüfer muss daran scheitern, eine Bestätigung zu fälschen, UND daran scheitern, ein Rollback zu überleben — auf einem echten Host, zweimal hintereinander.
Zwei aufeinanderfolgende saubere adversariale Durchgänge durch einen Prüfer, der die Korrektur nicht selbst geschrieben hat — nicht „die Muster, die wir jetzt erkennen, sind abgedeckt“, eine Behauptung, die bereits siebenmal aufgestellt und widerlegt wurde.
Nächste Schritte
- bitenforcer — Überblick — was
validateundapply --dry-runtun, mit echter Ausgabe. - bitcollector — die berichtende Hälfte des Paares, und woher die kontinuierlichen Nachweise zur Sicherheitslage kommen.
- Compliance-Frameworks — wie die Kontrollen zugeordnet werden.
War diese Seite hilfreich?