
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 se puede enviar señal o adjuntar.bpf — los mapas y programas eBPF de qué roles puede abrir un rol, o si
puede llamar a bpf(2) en absoluto.keyring — a los keyrings fs-verity de qué roles puede un rol añadir certificados,
o si puede escribir keyrings 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 mediante el xattr user.bpfj.policy.exec y queda
inscrito en él en el momento del exec. 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 central y los programas BPF: jailer, enforcers, analizador de políticas, helpers 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 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ósticos 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
anteriores no son compatibles.bpftool, y un compilador C++20. clang 20
y 21 no emiten addr_space_cast para un __builtin_memcpy desde memoria arena,
por lo que el verificador rechaza el jailer que construyen.CONFIG_BTRFS_FS=y en lugar de un módulo. Los
programas BPF leen los tipos de inodo de btrfs desde vmlinux.h, que el Makefile
genera a partir del BTF del kernel en ejecución.protoc, que Debian y Ubuntu distribuyen como libprotobuf-dev
y protobuf-compiler.openssl, fsverity y setfattr, además de los archivos
estáticos listados en el Makefile (STATIC=1).Establezca LIBBPF_CFLAGS / LIBBPF_LIBS si pkg-config no encuentra libbpf. Apunte
LIBARENA a la copia de libarena en cada compilación:
Cada make a continuación también necesita LIBARENA (consulta Requisitos), definido en la
línea de comandos o exportado en el entorno.
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
Todo el resultado se genera en 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 pruebas bajo sudo.
Las pruebas se ejecutan en serie de forma predeterminada porque la desvinculación concurrente de BPF LSM puede provocar un pánico en los kernels afectados. Use make test TEST_ARGS=Suite.Test para un caso específico, y solo opte por -j N o BPFJTEST_JOBS=N dentro de una VM desechable.
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
Los programas se fijan bajo /sys/fs/bpf/bpfj-pins por defecto. Use
--bpffs-path y --pin-dir para cambiar esto. 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
de BPF se escriben en stderr y los eventos estructurados en stdout. El logger
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
de pods, las variables y la propiedad de recursos rastreados, 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 en journal y se reproducen. El reemplazo falla de forma segura si las versiones de diseño
persistidas son incompatibles o el estado no se puede copiar de forma segura.
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