
Controllo di accesso obbligatorio e jailer basati su eBPF LSM
Controllo degli accessi obbligatorio basato su eBPF per Linux.
Questo progetto è una riscrittura completa del BpfJailer closed source ed è completamente sperimentale. Sfrutta funzionalità più recenti come bpf arena che non erano disponibili quando fu scritto il BpfJailer interno. Sono attesi problemi, che non sono idonei al bug bounty né considerati segnalazioni di sicurezza. Una volta adeguatamente valutato, sostituirà la versione interna closed source.
BpfJailer utilizza programmi eBPF LSM per inserire i processi in jail, chiamate
pod, ciascuna associata a un ruolo definito in una policy TOML. Un pod viene
ereditato attraverso fork ed exec. Funzionalità opzionali della policy:
kill e ptrace — ruoli/pod target a cui è consentito inviare segnali o
collegarsi.bpf — a quali mappe e programmi eBPF dei ruoli un ruolo può accedere,
o se può chiamare bpf(2) del tutto.keyring — a quali keyring fs-verity dei ruoli un ruolo può aggiungere
certificati, o se può scrivere sui keyring del tutto.I dinieghi e gli eventi del ciclo di vita vengono scritti in ring buffer
pinned. bpfjlog stampa la diagnostica BPF leggibile dall'uomo e gli eventi
strutturati e segue i ring buffer attraverso una sostituzione della policy a
runtime.
Un binario può rivendicare un ruolo tramite l'xattr user.bpfj.policy.exec ed
è iscritto a esso al momento dell'exec. Anche i processi in esecuzione possono
essere iscritti direttamente, e un processo non privilegiato può iscriversi da
solo tramite bpfjsrv/bpfjclient.
| Directory | Binario | Scopo |
|---|---|---|
bpfj/ | La libreria core e i programmi BPF: jailer, enforcer, parser della policy, helper C++ libbpf. | |
ctl/ | bpfjctl | Strumento generico per collegare, ricaricare, ispezionare e scollegare il jailer, e per iscrivere processi. |
cmd/ | bpfjcmd | bpfjctl con i suoi argomenti, e opzionalmente la sua policy, compilati all'interno. Ignora argv, quindi può essere linkato staticamente e firmato con fs-verity come singola unità. |
srv/ | bpfjsrv | Server attivato da socket che iscrive i chiamanti non privilegiati nei ruoli che lo consentono. |
client/ | bpfjclient | Client minimale per bpfjsrv, senza dipendenze da libbpf o dalla toolchain BPF. |
log/ | bpfjlog | Consumatore dei ring buffer pinned per diagnostica ed eventi strutturati. |
tests/ | bpfjtest | Suite di test. |
CONFIG_BPF_LSM=y e bpf nel
parametro di avvio lsm=). BpfJailer è testato solo su 6.16+, e i kernel
precedenti non sono supportati.bpftool, e un compilatore C++20.
clang 20 e 21 non emettono addr_space_cast per una __builtin_memcpy dalla
memoria arena, quindi il verifier rifiuta il jailer che compilano.CONFIG_BTRFS_FS=y anziché come modulo. I
programmi BPF leggono i tipi di inode btrfs da vmlinux.h, che il Makefile
genera dal BTF del kernel in esecuzione.protoc, che Debian e Ubuntu distribuiscono come
libprotobuf-dev e protobuf-compiler.openssl, fsverity e setfattr, più gli archivi
statici elencati nel Makefile (STATIC=1).Impostare LIBBPF_CFLAGS / LIBBPF_LIBS se pkg-config non riesce a trovare
libbpf. Puntare LIBARENA al checkout di libarena a ogni build:
Ogni make riportato di seguito richiede anche LIBARENA (vedi Requisiti), impostato sulla
riga di comando o esportato nell'ambiente.
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
Tutto l'output va in build/. Imposta BUILD= per compilare altrove, ad
esempio make BUILD=build-asan SANITIZE=address,undefined.
make test
I test devono essere eseguiti come root, perché ciascuno crea un mount namespace e
monta un bpffs. make test compila come utente che invoca il comando ed esegue solo il binario
di test sotto sudo.
I test vengono eseguiti in serie per impostazione predefinita perché il detach concorrente del BPF LSM può causare panic
sui kernel interessati. Usa make test TEST_ARGS=Suite.Test per un caso mirato, e
attiva -j N o BPFJTEST_JOBS=N solo all'interno di una VM usa e getta.
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
I programmi sono fissati sotto /sys/fs/bpf/bpfj-pins per impostazione predefinita. Usa
--bpffs-path e --pin-dir per modificarlo. Restano caricati finché non viene eseguito
detach.
bpfjctl wrap senza --drop-cap e con un --uid non root lascia il comando
in grado di rimuovere se stesso dalla jail. Vedi bpfjctl wrap --help.
Esegui sudo build/bpfjlog mentre il jailer è collegato per osservarlo. La diagnostica
BPF viene scritta su stderr e gli eventi strutturati su stdout. Il logger
si riconnette automaticamente quando replace sostituisce un nuovo insieme di mappe fissate.
replace carica un secondo jailer completo accanto a quello attivo, migra l'appartenenza
dei pod, le variabili e la proprietà delle risorse tracciate, quindi scambia atomicamente
gli alberi dei pin. Entrambi gli alberi restano collegati durante il passaggio di consegne,
i fork e l'enrollment sono coordinati con la migrazione, e le modifiche di proprietà sono
registrate nel journal e riprodotte. La sostituzione fallisce in modo sicuro se le versioni
del layout persistito sono incompatibili o se lo stato non può essere copiato in modo sicuro.
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