
Control de acceso obligatorio basado en eBPF LSM y jailer
Control de Acceso Obligatorio basado en eBPF para Linux.
Este proyecto es una reescritura completa del BpfJailer de código cerrado y es completamente experimental. Aprovecha características más recientes como bpf arena que no estaban disponibles cuando se escribió el BpfJailer interno. Se esperan problemas y no son elegibles para bug bounty ni se consideran hallazgos de seguridad. Una vez evaluado adecuadamente, reemplazará la versión interna de código cerrado.
BpfJailer utiliza programas eBPF LSM para colocar procesos en jaulas, llamadas pods, cada
una vinculada a un rol de una política TOML. Un pod se hereda a través de fork y
exec. Características opcionales de la política:
kill y ptrace — roles/pods objetivo a los que puede enviar señales o adjuntarse.bpf — los mapas y programas eBPF de qué roles puede abrir un rol, o si
puede llamar a bpf(2) en absoluto.keyring — los llaveros fs-verity de qué roles puede un rol agregar certificados,
o si puede escribir llaveros en absoluto.Las denegaciones y eventos de ciclo de vida se escriben en ring buffers fijados. bpfjlog
imprime los diagnósticos BPF legibles por humanos y los eventos estructurados y sigue
los ring buffers a través de un reemplazo de política en vivo.
Un binario puede reclamar un rol a través del xattr user.bpfj.policy.exec y se
inscribe en él en el momento de la ejecución. Los procesos en ejecución también pueden inscribirse directamente,
y un proceso sin privilegios puede inscribirse a sí mismo a través de bpfjsrv/bpfjclient.
| Directorio | Binario | Propósito |
|---|---|---|
bpfj/ | La biblioteca principal y los programas BPF: jailer, enforcers, analizador de políticas, ayudantes C++ de libbpf. | |
ctl/ | bpfjctl | Herramienta de propósito general para adjuntar, recargar, inspeccionar y desadjuntar el jailer, y para inscribir procesos. |
cmd/ | bpfjcmd | bpfjctl con sus argumentos, y opcionalmente su política, compilados dentro. Ignora argv, por lo que puede enlazarse estáticamente y firmarse con fs-verity como una sola unidad. |
srv/ | bpfjsrv | Servidor activado por socket que inscribe a los llamadores sin privilegios en roles que lo permiten. |
client/ | bpfjclient | Cliente mínimo para bpfjsrv, sin dependencia de libbpf ni de la cadena de herramientas BPF. |
log/ | bpfjlog | Consumidor de los ring buffers fijados de diagnóstico y eventos estructurados. |
tests/ | bpfjtest | Suite de pruebas. |
CONFIG_BPF_LSM=y y bpf en
el parámetro de arranque lsm=). BpfJailer solo se prueba en 6.16+, y los kernels
más antiguos no son compatibles.bpftool, y un compilador C++20.openssl, fsverity y setfattr, más los archivos
estáticos listados en el Makefile (STATIC=1).Establezca LIBBPF_CFLAGS / LIBBPF_LIBS si pkg-config no puede encontrar libbpf. Apunte
LIBARENA a la copia de libarena en cada compilación:
Cada make a continuación también necesita LIBARENA (ver Requisitos), establecido en la
línea de comandos o exportado en el entorno.
make # build/bpfjctl
make STATIC=1 # bpfjctl sin dependencias de objetos compartidos
make client # build/bpfjclient, no se necesita la cadena de herramientas BPF
make log # build/bpfjlog
make signing-key # generar una clave de firma de desarrollo y certificado
make signed SIGNING_KEY=... SIGNING_CERT=... # bpfjctl estático, firmado con fs-verity
make srv SIGNING_KEY=... SIGNING_CERT=... # bpfjsrv estático, firmado
make cmd SIGNING_KEY=... SIGNING_CERT=... \
CMD_ARGS="replace-compiled" CMD_POLICY=policy.toml CMD_ROLE=bpfjailer
make clean
Toda la salida va bajo build/. Establezca BUILD= para compilar en otro lugar, por
ejemplo make BUILD=build-asan SANITIZE=address,undefined.
make test
Las pruebas deben ejecutarse como root, porque cada una crea un espacio de nombres de montaje y
monta un bpffs. make test compila como el usuario que lo invoca y ejecuta solo el binario de prueba
bajo sudo.
Las pruebas se ejecutan en serie por defecto porque el desadjunte concurrente de BPF LSM puede provocar un pánico
en los kernels afectados. Use make test TEST_ARGS=Suite.Test para un caso enfocado, y
solo opte por -j N o BPFJTEST_JOBS=N dentro de una VM desechable.
sudo bpfjctl check policy.toml # analizar una política e informar qué contiene
sudo bpfjctl attach policy.toml # cargar y fijar el jailer
sudo bpfjctl replace policy.toml # recargar sin liberar las tareas encarceladas
sudo bpfjctl wrap ROLE USER_ID -- CMD # ejecutar CMD en un nuevo pod
sudo bpfjctl enroll ROLE USER_ID PID [NAME=VALUE...] # inscribir con variables
sudo bpfjctl show PID # pods en los que está un proceso
sudo bpfjctl list # cada pod y sus procesos
sudo bpfjctl detach # desfijar y descargar
Los programas se fijan bajo /sys/fs/bpf/bpfj-pins por defecto. Use
--bpffs-path y --pin-dir para cambiarlo. Permanecen cargados hasta que se ejecuta
detach.
bpfjctl wrap sin --drop-cap y un --uid no root deja el comando
capaz de eliminarse a sí mismo de la jaula. Consulte bpfjctl wrap --help.
Ejecute sudo build/bpfjlog mientras el jailer está adjunto para observarlo. Los diagnósticos
BPF se escriben en stderr y los eventos estructurados en stdout. El registrador
se reconecta automáticamente cuando replace intercambia un nuevo conjunto de mapas fijados.
replace carga un segundo jailer completo junto al activo, migra la pertenencia a pods,
las variables y la propiedad de recursos rastreada, luego intercambia atómicamente
los árboles de fijación. Ambos árboles permanecen adjuntos durante el traspaso, los forks y
la inscripción se coordinan con la migración, y los cambios de propiedad se registran y reproducen. El reemplazo falla de forma segura si las versiones de diseño persistidas
son incompatibles o el estado no puede copiarse de forma segura.
base-role = "floor" # opcional: inscribir cada proceso en el host
vars = ["vm_uuid"] # nombres de variables conocidos
[certs]
corp-ca = "MIIDXTCCAkWgAwIBAgIJAK..." # certificado PEM o DER en base64
[roles.floor]
any = true # rol base abierto solo para seguimiento