
Controllo di accesso obbligatorio e jailer basati su eBPF LSM
Controllo di accesso 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 è stato scritto il BpfJailer interno. Sono attesi problemi che non sono idonei al bug bounty né considerati come 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 legata a un ruolo da una policy TOML. Un pod viene ereditato
attraverso fork ed exec. Funzionalità opzionali della policy:
kill e ptrace — ruoli/pod target a cui questo può inviare segnali o
connettersi.bpf — a quali ruoli appartengono le mappe e i programmi eBPF che un
ruolo può aprire, o se può chiamare bpf(2) del tutto.keyring — a quali ruoli appartengono i keyring fs-verity a cui un ruolo
può aggiungere certificati, o se può scrivere 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 di policy a
caldo.
Un binario può rivendicare un ruolo tramite l'xattr user.bpfj.policy.exec e
viene iscritto ad 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 di policy, helper C++ libbpf. | |
ctl/ | bpfjctl | Strumento generico per attach, reload, ispezione e detach del jailer, e per l'iscrizione dei processi. |
cmd/ | bpfjcmd | bpfjctl con i suoi argomenti, e opzionalmente la sua policy, compilati dentro. 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 | Consumer per i ring buffer pinned di diagnostica e eventi strutturati. |
tests/ | bpfjtest | Suite di test. |
CONFIG_BPF_LSM=y e bpf nel
parametro di boot lsm=). BpfJailer è testato solo su 6.16+, e i kernel più
vecchi non sono supportati.bpftool, e un compilatore C++20.openssl, fsverity e setfattr, più gli archivi statici
elencati nel Makefile (STATIC=1).Imposta LIBBPF_CFLAGS / LIBBPF_LIBS se pkg-config non riesce a trovare
libbpf. Punta LIBARENA al checkout di libarena su ogni build:
Ogni make qui sotto necessita anche di LIBARENA (vedi Requisiti), impostato
sulla riga di comando o esportato nell'ambiente.
make # build/bpfjctl
make STATIC=1 # bpfjctl senza dipendenze da shared object
make client # build/bpfjclient, nessuna toolchain BPF necessaria
make log # build/bpfjlog
make signing-key # genera una chiave di firma di sviluppo e un certificato
make signed SIGNING_KEY=... SIGNING_CERT=... # bpfjctl statico, firmato fs-verity
make srv SIGNING_KEY=... SIGNING_CERT=... # bpfjsrv statico, firmato
make cmd SIGNING_KEY=... SIGNING_CERT=... \
CMD_ARGS="replace-compiled" CMD_POLICY=policy.toml CMD_ROLE=bpfjailer
make clean
Tutto l'output va sotto build/. Imposta BUILD= per compilare altrove, per
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 invocante ed esegue
solo il binario di test sotto sudo.
I test vengono eseguiti in serie per impostazione predefinita perché il detach
concorrente di BPF LSM può mandare in panic i kernel interessati. Usa
make test TEST_ARGS=Suite.Test per un caso mirato, e opta per -j N o
BPFJTEST_JOBS=N solo all'interno di una VM usa e getta.
sudo bpfjctl check policy.toml # analizza una policy e riporta cosa contiene
sudo bpfjctl attach policy.toml # carica e pinna il jailer
sudo bpfjctl replace policy.toml # ricarica senza rilasciare i task in jail
sudo bpfjctl wrap ROLE USER_ID -- CMD # esegue CMD in un nuovo pod
sudo bpfjctl enroll ROLE USER_ID PID [NAME=VALUE...] # iscrive con variabili
sudo bpfjctl show PID # pod in cui si trova un processo
sudo bpfjctl list # ogni pod e i suoi processi
sudo bpfjctl detach # unpin e unload
I programmi sono pinned sotto /sys/fs/bpf/bpfj-pins per impostazione
predefinita. Usa --bpffs-path e --pin-dir per cambiarlo. Rimangono caricati
finché non viene eseguito detach.
bpfjctl wrap senza --drop-cap e con un --uid non-root lascia il comando
in grado di rimuoversi da solo dalla jail. Vedi bpfjctl wrap --help.
Esegui sudo build/bpfjlog mentre il jailer è attaccato 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 pinned.
replace carica un secondo jailer completo accanto a quello attivo, migra
l'appartenenza ai pod, le variabili e la proprietà delle risorse tracciate,
poi scambia atomicamente gli alberi di pin. Entrambi gli alberi rimangono
attaccati durante il passaggio, fork e iscrizioni sono coordinati con la
migrazione, e i cambi di proprietà sono journaled e riprodotti. La sostituzione
fallisce in modo sicuro se le versioni di layout persistite sono incompatibili
o lo stato non può essere copiato in sicurezza.
base-role = "floor" # opzionale: iscrive ogni processo sull'host
vars = ["vm_uuid"] # nomi di variabili noti
[certs]
corp-ca = "MIIDXTCCAkWgAwIBAgIJAK..." # certificato PEM o base64 DER
[roles.floor]
any = true # apre il ruolo base di solo tracciamento