
eBPF-LSM-basierte obligatorische Zugriffskontrolle und Jailer
eBPF-basierte Mandatory Access Control für Linux.
Dieses Projekt ist eine vollständige Neuimplementierung des quelloffenen BpfJailer und vollständig experimentell. Es nutzt neuere Funktionen wie bpf arena, die nicht verfügbar waren, als der interne BpfJailer geschrieben wurde. Probleme sind zu erwarten und sind nicht für Bug Bounties 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, zu stecken, 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 dürfen.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 beschreiben 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 eine Rolle über das xattr user.bpfj.policy.exec beanspruchen und wird
zur Ausführungszeit darin eingetragen. Laufende Prozesse können auch direkt eingetragen werden,
und ein unprivilegierter Prozess kann sich selbst über bpfjsrv/bpfjclient eintragen.
| 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 Abhängen des Jailers sowie zum Eintragen von Prozessen. |
cmd/ | bpfjcmd | bpfjctl mit seinen Argumenten und optional seiner Richtlinie, einkompiliert. 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 einträgt, die dies erlauben. |
client/ | bpfjclient | Minimaler Client für bpfjsrv, ohne libbpf- oder BPF-Toolchain-Abhängigkeit. |
log/ | bpfjlog | Konsument für die gepinnten Ringpuffer für Diagnosen und strukturierte Ereignisse. |
tests/ | bpfjtest | Testsuite. |
CONFIG_BPF_LSM=y und bpf im
Boot-Parameter lsm=). BpfJailer wird nur auf 6.16+ getestet, und ältere
Kernel werden nicht unterstützt.bpftool und einen C++20-Compiler. clang 20
und 21 erzeugen kein addr_space_cast für ein __builtin_memcpy aus Arena-Speicher,
sodass der Verifier den von ihnen gebauten Jailer ablehnt.CONFIG_BTRFS_FS=y statt als Modul. Die
BPF-Programme lesen btrfs-Inode-Typen aus vmlinux.h, das das Makefile
aus der BTF des laufenden Kernels generiert.protoc, die Debian und Ubuntu als libprotobuf-dev
und protobuf-compiler ausliefern.openssl, fsverity und setfattr sowie die statischen
Archive, die im Makefile aufgeführt sind (STATIC=1).Setzen Sie LIBBPF_CFLAGS / LIBBPF_LIBS, falls pkg-config libbpf nicht finden kann. Richten Sie
LIBARENA bei jedem Build auf den libarena-Checkout:
Jedes unten aufgeführte make benötigt außerdem LIBARENA (siehe Anforderungen), entweder auf der
Kommandozeile gesetzt oder in der Umgebung exportiert.
make # build/bpfjctl
make STATIC=1 # bpfjctl with no shared object dependencies
make client # build/bpfjclient, no BPF toolchain needed
make log # build/bpfjlog
make signing-key # generate a development signing key and certificate
make signed SIGNING_KEY=... SIGNING_CERT=... # static, fs-verity signed bpfjctl
make srv SIGNING_KEY=... SIGNING_CERT=... # static, signed bpfjsrv
make cmd SIGNING_KEY=... SIGNING_CERT=... \
CMD_ARGS="replace-compiled" CMD_POLICY=policy.toml CMD_ROLE=bpfjailer
make clean
Die gesamte Ausgabe erfolgt unter build/. Setzen Sie BUILD=, um an einem anderen Ort zu bauen,
zum Beispiel make BUILD=build-asan SANITIZE=address,undefined.
make test
Die Tests müssen als root ausgeführt werden, da jeder einzelne einen Mount-Namespace erstellt und ein bpffs einbindet. make test baut als aufrufender Benutzer und führt nur die Test-Binary unter sudo aus.
Tests laufen standardmäßig seriell, da gleichzeitiges BPF-LSM-Detach betroffene Kernel zum Panic bringen 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 # parse a policy and report what it holds
sudo bpfjctl attach policy.toml # load and pin the jailer
sudo bpfjctl replace policy.toml # reload without releasing jailed tasks
sudo bpfjctl wrap ROLE POD_ID -- CMD # run CMD in a new pod
sudo bpfjctl enroll ROLE POD_ID PID [NAME=VALUE...] # enroll with variables
sudo bpfjctl show PID # pods a process is in
sudo bpfjctl list # every pod and its processes
sudo bpfjctl detach # unpin and unload
Die Programme werden standardmäßig unter /sys/fs/bpf/bpfj-pins angeheftet. 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 eine nicht-root --uid hinterlässt den Befehl
in der Lage, sich selbst aus der 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 auf stderr und strukturierte Ereignisse auf stdout geschrieben. Der Logger
verbindet sich automatisch neu, wenn replace einen neuen Satz angehefteter Maps austauscht.
replace lädt einen vollständigen zweiten Jailer neben dem aktiven, migriert die Pod-
Zugehörigkeit, Variablen und verfolgte Ressourcen-Eigentümerschaft und tauscht dann atomar
die Pin-Bäume aus. Beide Bäume bleiben während der Übergabe angehängt, Forks und
Enrollment werden mit der Migration koordiniert, und Eigentümerschaftsänderungen werden
journalisiert und erneut abgespielt. Der Ersatz schlägt geschlossen fehl, wenn persistierte Layout-Versionen
inkompatibel sind oder der Zustand nicht sicher kopiert werden kann.
base-role = "floor" # optional: enroll every process on the host
vars = ["vm_uuid"] # known variable names
[certs]
corp-ca = "MIIDXTCCAkWgAwIBAgIJAK..." # PEM or base64 DER certificate
[roles.floor]
any = true # open tracking-only base role