
eBPF-LSM-basierte obligatorische Zugriffskontrolle und Jailer
eBPF-basierte Mandatory Access Control für Linux.
Dieses Projekt ist eine vollständige Neufassung des Closed-Source-BpfJailer und vollständig experimentell. Es nutzt neuere Funktionen wie bpf arena, die zum Zeitpunkt der Erstellung des internen BpfJailer nicht verfügbar waren. Probleme sind zu erwarten und sind nicht für Bug Bounty berechtigt oder gelten als Sicherheitsbefunde. Nach ordnungsgemäßer Evaluierung wird es die interne Closed-Source-Version ersetzen.
BpfJailer verwendet eBPF-LSM-Programme, um Prozesse in Jails, sogenannte Pods,
einzusperren, die jeweils an eine Rolle aus einer TOML-Richtlinie gebunden sind.
Ein Pod wird über fork und exec hinweg vererbt. Optionale
Richtlinienfunktionen:
kill und ptrace — Zielrollen/-pods, die signalisiert oder an die
angehängt werden darf.bpf — auf welche eBPF-Maps und -Programme welcher Rollen eine Rolle
zugreifen darf oder ob sie überhaupt bpf(2) aufrufen darf.keyring — zu welchen fs-verity-Keyrings welcher Rollen eine Rolle
Zertifikate hinzufügen darf oder ob sie überhaupt Keyrings schreiben darf.Ablehnungen und Lebenszyklusereignisse werden in gepinnte Ringpuffer
geschrieben. bpfjlog gibt die menschenlesbaren BPF-Diagnosen und
strukturierten Ereignisse aus und folgt den Ringpuffern über einen
Live-Richtlinienaustausch hinweg.
Eine Binärdatei kann über das xattr user.bpfj.policy.exec eine Rolle
beanspruchen und wird zur Ausführungszeit darin eingeschrieben. Laufende
Prozesse können auch direkt eingeschrieben werden, und ein unprivilegierter
Prozess kann sich selbst über bpfjsrv/bpfjclient einschreiben.
| Verzeichnis | Binärdatei | Zweck |
|---|---|---|
bpfj/ | Die Kernbibliothek und BPF-Programme: Jailer, Enforcer, Richtlinienparser, libbpf-C++-Helfer. | |
ctl/ | bpfjctl | Allzweckwerkzeug zum Anhängen, Neuladen, Inspizieren und Abtrennen des Jailers sowie zum Einschreiben von Prozessen. |
cmd/ | bpfjcmd | bpfjctl mit einkompilierten Argumenten und optional einkompilierter Richtlinie. Es ignoriert argv, sodass es statisch gelinkt und als einzelne Einheit fs-verity-signiert werden kann. |
srv/ | bpfjsrv | Socket-aktivierter Server, der unprivilegierte Aufrufer in Rollen einschreibt, die dies erlauben. |
client/ | bpfjclient | Minimaler Client für bpfjsrv, ohne libbpf- oder BPF-Toolchain-Abhängigkeit. |
log/ | bpfjlog | Konsument für die gepinnten Diagnose- und strukturierten Ereignis-Ringpuffer. |
tests/ | bpfjtest | Testsuite. |
CONFIG_BPF_LSM=y und bpf
im Boot-Parameter lsm=). BpfJailer wird nur auf 6.16+ getestet, ältere
Kernel werden nicht unterstützt.bpftool und ein C++20-Compiler.openssl, fsverity und setfattr sowie die im
Makefile aufgeführten statischen Archive (STATIC=1).Setzen Sie LIBBPF_CFLAGS / LIBBPF_LIBS, falls pkg-config libbpf nicht
finden kann. Verweisen Sie LIBARENA bei jedem Build auf den
libarena-Checkout:
Jedes make unten benötigt außerdem LIBARENA (siehe Anforderungen), entweder
auf der Kommandozeile gesetzt oder in der Umgebung exportiert.
make # build/bpfjctl
make STATIC=1 # bpfjctl ohne Abhängigkeiten von Shared Objects
make client # build/bpfjclient, keine BPF-Toolchain nötig
make log # build/bpfjlog
make signing-key # generiert einen Entwicklungssignaturschlüssel und ein Zertifikat
make signed SIGNING_KEY=... SIGNING_CERT=... # statisches, fs-verity-signiertes bpfjctl
make srv SIGNING_KEY=... SIGNING_CERT=... # statisches, signiertes bpfjsrv
make cmd SIGNING_KEY=... SIGNING_CERT=... \
CMD_ARGS="replace-compiled" CMD_POLICY=policy.toml CMD_ROLE=bpfjailer
make clean
Alle Ausgaben landen unter build/. Setzen Sie BUILD=, um woanders zu bauen,
zum Beispiel make BUILD=build-asan SANITIZE=address,undefined.
make test
Die Tests müssen als root ausgeführt werden, da jeder einen Mount-Namespace
erstellt und ein bpffs mountet. make test baut als aufrufender Benutzer und
führt nur die Testbinärdatei unter sudo aus.
Tests laufen standardmäßig seriell, da gleichzeitiges BPF-LSM-Detach betroffene
Kernel in Panik versetzen kann. Verwenden Sie make test TEST_ARGS=Suite.Test
für einen fokussierten Fall und aktivieren Sie -j N oder BPFJTEST_JOBS=N
nur innerhalb einer Wegwerf-VM.
sudo bpfjctl check policy.toml # parst eine Richtlinie und meldet, was sie enthält
sudo bpfjctl attach policy.toml # lädt und pinnt den Jailer
sudo bpfjctl replace policy.toml # lädt neu, ohne eingesperrte Tasks freizugeben
sudo bpfjctl wrap ROLE USER_ID -- CMD # führt CMD in einem neuen Pod aus
sudo bpfjctl enroll ROLE USER_ID PID [NAME=VALUE...] # schreibt mit Variablen ein
sudo bpfjctl show PID # Pods, in denen sich ein Prozess befindet
sudo bpfjctl list # jeder Pod und seine Prozesse
sudo bpfjctl detach # entpinnt und entlädt
Die Programme werden standardmäßig unter /sys/fs/bpf/bpfj-pins gepinnt.
Verwenden Sie --bpffs-path und --pin-dir, um dies zu ändern. Sie bleiben
geladen, bis detach ausgeführt wird.
bpfjctl wrap ohne --drop-cap und mit einer Nicht-root---uid lässt den
Befehl in der Lage, sich selbst aus dem Jail zu entfernen. Siehe
bpfjctl wrap --help.
Führen Sie sudo build/bpfjlog aus, während der Jailer angehängt ist, um ihn zu
beobachten. BPF-Diagnosen werden nach stderr und strukturierte Ereignisse nach
stdout geschrieben. Der Logger verbindet sich automatisch neu, wenn replace
einen neuen Satz gepinnter Maps einwechselt.
replace lädt einen vollständigen zweiten Jailer neben dem aktiven, migriert
Pod-Mitgliedschaft, Variablen und verfolgte Ressourceneigentümerschaft und
tauscht dann die Pin-Bäume atomar aus. Beide Bäume bleiben während der Übergabe
angehängt, Forks und Einschreibungen werden mit der Migration koordiniert, und
Eigentümerschaftsänderungen werden journalisiert und wiedergegeben. Der Ersatz
schlägt geschlossen fehl, wenn persistierte Layout-Versionen inkompatibel sind
oder der Zustand nicht sicher kopiert werden kann.
base-role = "floor" # optional: jeden Prozess auf dem Host einschreiben
vars = ["vm_uuid"] # bekannte Variablennamen
[certs]
corp-ca = "MIIDXTCCAkWgAwIBAgIJAK..." # PEM- oder base64-DER-Zertifikat
[roles.floor]
any = true # offene Basisrolle nur zur Verfolgung