
Jailer es un sistema de aislamiento de procesos basado en eBPF que proporciona control de acceso obligatorio (MAC) para Linux. Realiza un seguimiento de los procesos mediante mapas de almacenamiento de tareas BPF y aplica políticas basadas en roles sobre el acceso a archivos, las operaciones de red y la ejecución de procesos.
Advertencia: Este proyecto está en desarrollo intensivo y NO está listo para uso en producción. Las APIs, formatos de políticas y el comportamiento pueden cambiar sin previo aviso. Úselo solo para pruebas y experimentación.
Nota: Esta es una implementación independiente y no es el mismo proyecto que la solución de Meta. Si bien BpfJailer es funcionalmente similar y está inspirado en la idea y el diseño originales de Liam Wisehart, Justin Nga, Carl El Khoury, Mansee Chadha en Meta, este es un código base separado desarrollado de forma independiente. Agradecemos la visión y los conceptos fundamentales que inspiraron este trabajo.
Jailer es un sistema de confinamiento de procesos basado en eBPF que proporciona control de acceso obligatorio (MAC) para Linux. Realiza un seguimiento de los procesos mediante mapas BPF task_storage y aplica políticas basadas en roles sobre acceso a archivos, operaciones de red y ejecución de procesos.

BPF_MAP_TYPE_TASK_STORAGE)# Verificar la configuración actual del kernel
zcat /proc/config.gz 2>/dev/null || cat /boot/config-$(uname -r)
Opciones requeridas:
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_LSM=y
CONFIG_DEBUG_INFO_BTF=y
BPF LSM debe estar habilitado en los parámetros de arranque del kernel:
# Verificar si BPF LSM está activo
cat /sys/kernel/security/lsm
# Debería incluir "bpf" en la lista
# Si no, agregar a los parámetros de arranque del kernel:
# Editar /etc/default/grub y añadir a GRUB_CMDLINE_LINUX:
# lsm=lockdown,capability,landlock,yama,apparmor,bpf
# Luego actualizar grub y reiniciar:
sudo update-grub
sudo reboot
Para sistemas Ubuntu/Debian, también se puede usar:
# Crear un script para habilitar BPF LSM
cat > /tmp/enable_bpf_lsm.sh << 'EOF'
#!/bin/bash
GRUB_FILE="/etc/default/grub"
if grep -q "lsm=" "$GRUB_FILE"; then
sudo sed -i 's/lsm=[^""]*/lsm=lockdown,capability,landlock,yama,apparmor,bpf/' "$GRUB_FILE"
else
sudo sed -i 's/GRUB_CMDLINE_LINUX="\(.*\)"/GRUB_CMDLINE_LINUX="\1 lsm=lockdown,capability,landlock,yama,apparmor,bpf"/' "$GRUB_FILE"
fi
sudo update-grub
echo "BPF LSM habilitado. Por favor, reinicie."
EOF
chmod +x /tmp/enable_bpf_lsm.sh
sudo /tmp/enable_bpf_lsm.sh
# Instalar Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Instalar dependencias de compilación (Ubuntu/Debian)
sudo apt-get install -y clang llvm libelf-dev linux-headers-$(uname -r)
# Instalar target BPF para Rust
rustup target add bpfel-unknown-none
cd bpfjail
# Compilar programas BPF
cd bpfjailer-bpf && cargo build --release && cd ..
# Compilar demonio y cliente
cargo build --release
# Ejecutar como root (carga config/policy.json si existe)
sudo RUST_LOG=info ./target/release/bpfjailer-daemon
Salida esperada:
[INFO] Demonio BpfJailer iniciando...
[INFO] Cargando programas eBPF de BpfJailer con libbpf-rs...
[INFO] ✓ mapa pending_enrollments disponible para inscripción
[INFO] ✓ mapa network_rules disponible para filtrado de puerto/protocolo
[INFO] ✓ mapa task_storage creado exitosamente
[INFO] ✓ Programa task_alloc adjuntado
[INFO] ✓ Programa file_open adjuntado
[INFO] ✓ Programa socket_bind adjuntado
[INFO] ✓ Programa socket_connect adjuntado
[INFO] ✓ Programa bprm_check_security adjuntado
[INFO] Inicializado con roles predeterminados: restricted (1), permissive (2)
[INFO] Política cargada desde config/policy.json
[INFO] Cargados 5 roles
[INFO] Servidor de inscripción escuchando en /run/bpfjailer/enrollment.sock
# Ejecutar pruebas de vulnerabilidad SIN jail (se muestran ataques exitosos)
sudo python3 tests/vulnerable_apps/run_tests.py
# Ejecutar pruebas de vulnerabilidad CON rol restringido (se muestran ataques bloqueados)
sudo python3 tests/vulnerable_apps/run_tests.py --role 1
# Ejecutar una prueba específica
sudo python3 tests/vulnerable_apps/run_tests.py --role 1 --test path
# Listar pruebas y roles disponibles
sudo python3 tests/vulnerable_apps/run_tests.py --list
Ejemplo de salida con rol restringido:
============================================================
PRUEBA: Path Traversal / Lectura Arbitraria de Archivos
============================================================
Intentando leer /etc/passwd mediante path traversal...
BLOQUEADO - Permiso denegado (BpfJailer bloqueó el acceso al archivo)
============================================================
PRUEBA: Inyección de Comandos
============================================================
Intentando inyección de comandos...
BLOQUEADO - Permiso denegado (BpfJailer bloqueó exec)
============================================================
PRUEBA: Reverse Shell / Exfiltración de Datos
============================================================
Prueba 1: Conexión reverse shell a 127.0.0.1:4444
BLOQUEADO - Permiso denegado (BpfJailer bloqueó connect)
#!/usr/bin/env python3
import socket
import json
import os
# Conectar al demonio
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect("/run/bpfjailer/enrollment.sock")
# Inscribir con rol restringido (ID 1)
request = {"Enroll": {"pod_id": 1, "role_id": 1}}
sock.send((json.dumps(request) + "\n").encode())
response = sock.recv(4096).decode()
print(f"Inscripción: {response}")
sock.close()
# Intentar leer un archivo (debería estar bloqueado)
try:
open("/etc/passwd").read()
print("Acceso a archivo: PERMITIDO")
except PermissionError:
print("Acceso a archivo: BLOQUEADO")
BpfJailer carga los roles desde un archivo de política JSON. El demonio busca el archivo de política en este orden:
$BPFJAILER_POLICY/etc/bpfjailer/policy.jsonconfig/policy.json (relativo al directorio de trabajo){
"roles": {
"restricted": {
"id": 1,
"name": "restricted",
"flags": {
"allow_file_access": false,
"allow_network": false,
"allow_exec": false
},
"network_rules": []
},
"webserver": {
"id": 3,
"name": "webserver",
"flags": {
"allow_file_access": true,
"allow_network": true,
"allow_exec": false
},
"network_rules": [
{"protocol": "tcp", "port": 80, "allow": true},
{"protocol": "tcp", "port": 443, "allow": true}
]
}
},
"pods": []
}
BpfJailer admite un control de red detallado con reglas TCP/UDP por puerto.
mapa network_rules:
Clave: { role_id, port, protocol, direction }
Valor: allowed (1) o denied (0)
// En el código del demonio:
use bpfjailer_daemon::process_tracker::{PROTO_TCP, DIR_CONNECT};
// Permitir solo TCP connect a los puertos 80 y 443
process_tracker.add_network_rule(RoleId(1), 80, PROTO_TCP, DIR_CONNECT, true)?;
process_tracker.add_network_rule(RoleId(1), 443, PROTO_TCP, DIR_CONNECT, true)?;
Se pueden especificar rangos de puertos en policy.json usando port_start y port_end:
{
"network_rules": [
{"protocol": "tcp", "port": 443, "allow": true},
{"protocol": "tcp", "port_start": 8000, "port_end": 8100, "allow": true}
]
}
| Campo | Descripción |
|---|---|
port | Puerto único (ej. 80) |
port_start + port_end | Rango de puertos (ej. 8000-8100) |
Nota: Los rangos grandes (>1000 puertos) consumen muchas entradas del mapa BPF. Se registra una advertencia para rangos que excedan 1000 puertos.
┌─────────────────────────────────────────────────────────────┐
│ Espacio de Usuario │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ Cliente │───▶│ bpfjailer-daemon │ │
│ │ (inscribir)│ │ │ │
│ └─────────────┘ │ - PolicyManager │ │
│ │ - ProcessTracker│ │
│ │ - EnrollmentSvr │ │
│ └────────┬─────────┘ │
│ │ escribe en │
│ ▼ │
├─────────────────────────────────────────────────────────────┤
│ Mapas BPF │
│ ┌──────────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │pending_enrollments│ │ role_flags │ │ task_storage │ │
│ │ (PID → info) │ │ (rol→flags) │ │(tarea→info) │ │
│ └──────────────────┘ └─────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Hooks BPF LSM │
│ ┌────────────┐ ┌───────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ task_alloc │ │ file_open │ │socket_bind/ │ │bprm_check│ │
│ │(herencia │ │(migración │ │ connect │ │_security │ │
│ │ + init) │ │ + check) │ │ (check) │ │ (check) │ │
│ └────────────┘ └───────────┘ └─────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
/run/bpfjailer/enrollment.sock{"Enroll": {"pod_id": N, "role_id": M}}pending_enrollments[PID] y role_flags[role_id]task_storagetask_storage + role_flags para su aplicacióntask_allocBpfJailer admite dos modos de instalación:
Utiliza un demonio en ejecución para la inscripción y gestión de políticas:
# Instalar servicio systemd
sudo cp config/bpfjailer-daemon.service /etc/systemd/system/
sudo cp target/release/bpfjailer-daemon /usr/sbin/
sudo mkdir -p /etc/bpfjailer
sudo cp config/policy.json /etc/bpfjailer/
# Habilitar e iniciar
sudo systemctl daemon-reload
sudo systemctl enable bpfjailer-daemon
sudo systemctl start bpfjailer-daemon
Características:
Carga los programas BPF en el arranque temprano y sale. Los programas permanecen activos hasta el reinicio:
# Instalar servicio bootstrap
sudo cp config/bpfjailer-bootstrap.service /etc/systemd/system/
sudo cp target/release/bpfjailer-bootstrap /usr/sbin/
sudo mkdir -p /etc/bpfjailer
sudo cp config/policy.json /etc/bpfjailer/
# Habilitar (se ejecutará en el próximo arranque)
sudo systemctl daemon-reload
sudo systemctl enable bpfjailer-bootstrap
# O ejecutar manualmente ahora
sudo bpfjailer-bootstrap
Características:
/sys/fs/bpf/bpfjailer/Verificar programas fijados:
ls -la /sys/fs/bpf/bpfjailer/
ls -la /sys/fs/bpf/bpfjailer/maps/
ls -la /sys/fs/bpf/bpfjailer/progs/
Ver eventos de auditoría:
# Los eventos se emiten al buffer perf, recogidos por journald
journalctl -f | grep bpfjailer
# Verificar versión del kernel (necesita 5.11+)
uname -r
# Verificar que BPF LSM esté activo
cat /sys/kernel/security/lsm | grep bpf
# Verificar que BTF esté disponible
ls -la /sys/kernel/btf/vmlinux
# Verificar si los programas BPF se pueden cargar
sudo bpftool prog list
# Verificar capacidades
sudo capsh --print | grep cap_bpf
# Asegurarse de que el demonio se ejecute como root
ps aux | grep bpfjailer
# Verificar permisos del socket
ls -la /run/bpfjailer/enrollment.sock
Además de la inscripción por socket Unix, BpfJailer admite inscripción automática basada en:
Auto-inscribir todos los procesos que ejecuten un binario específico:
{
"exec_enrollments": [
{
"executable_path": "/usr/bin/nginx",
"pod_id": 1000,
"role": "webserver"
}
]
}
Cuando cualquier proceso ejecuta /usr/bin/nginx, se inscribe automáticamente con el rol webserver. Esto se empareja mediante el inodo del ejecutable, por lo que los enlaces simbólicos y duros se manejan correctamente.
Auto-inscribir todos los procesos en un cgroup específico:
{
"cgroup_enrollments": [
{
"cgroup_path": "/sys/fs/cgroup/bpfjailer/sandbox",
"pod_id": 2000,
"role": "sandbox"
}
]
}
Crear el cgroup y mover procesos a él:
# Crear cgroup
sudo mkdir -p /sys/fs/cgroup/bpfjailer/sandbox
# Mover proceso al cgroup
echo $$ | sudo tee /sys/fs/cgroup/bpfjailer/sandbox/cgroup.procs
# El proceso ahora está auto-inscrito con el rol sandbox
Establecer atributos extendidos en ejecutables para información de inscripción:
# Establecer xattrs de inscripción
sudo setfattr -n user.bpfjailer.pod_id -v $(printf '\x01\x00\x00\x00\x00\x00\x00\x00') /ruta/al/binario
sudo setfattr -n user.bpfjailer.role_id -v $(printf '\x03\x00\x00\x00') /ruta/al/binario
# Verificar xattrs
getfattr -d /ruta/al/binario
En tiempo de exec (hook LSM bprm_check_security):
exec_enrollmentcgroup_enrollmenttask_storage con pod_id y role_idLa inscripción persiste a través de fork/exec mediante el hook task_alloc
Las reglas de política (red, ruta, exec) se aplican según el role_id
GPL-2.0 (requerido para programas BPF)
| Característica | Estado | Descripción |
|---|
| Seguimiento de Procesos | ✅ Funcionando | Rastrea procesos mediante el mapa BPF task_storage |
| Inscripción por Socket | ✅ Funcionando | Los procesos se inscriben mediante una API de socket Unix |
| Políticas Basadas en Roles | ✅ Funcionando | Roles restringido y permisivo |
| Control de Acceso a Archivos | ✅ Funcionando | Bloquear/permitir operaciones de apertura de archivos |
| Herencia de Jail | ✅ Funcionando | Los procesos hijos heredan el jail del padre |
| Control de Red | ✅ Funcionando | Bloquear/permitir bind/connect de sockets |
| Filtrado por Puerto/Protocolo | ✅ Funcionando | Reglas de permitir/denegar TCP/UDP por puerto |
| Control de Ejecución | ✅ Funcionando | Bloquear/permitir ejecución de procesos |
| Coincidencia de Rutas | ✅ Funcionando | Recorrido de dentry con invalidación de caché |
| Binarios Firmados | 🚧 Esqueleto | Validación de firma de binarios (no implementado) |
| Inscripción Alternativa | ✅ Funcionando | Auto-inscripción por ejecutable, cgroup o xattr |
| Modo Sin Demonio | ✅ Funcionando | El binario bootstrap fija los programas en el arranque temprano |
| Eventos de Auditoría | ✅ Funcionando | Buffer perf para integración con systemd-journald |
| Prueba | Vulnerabilidad | Mitigado Por |
|---|
path_traversal | Lectura arbitraria de archivos mediante ../ | restricted, isolated |
command_injection | Ejecución de comandos de shell | restricted, webserver, isolated |
reverse_shell | Conexiones salientes al atacante | restricted, isolated |
ssrf | Acceso a servicios internos/metadatos en la nube | restricted, isolated |
arbitrary_write | Escritura en rutas sensibles | restricted |
crypto_miner | Descargar + ejecutar + conectar a pool | restricted, webserver |
privilege_escalation | Leer shadow, escribir sudoers | restricted |
| Bandera | Descripción |
|---|
allow_file_access | Permitir operaciones de apertura de archivos |
allow_network | Permitir bind/connect de sockets |
allow_exec | Permitir ejecución de procesos |
allow_setuid | Permitir operaciones setuid |
allow_ptrace | Permitir operaciones ptrace |
| ID de Rol | Nombre | Acceso a Archivos | Red | Ejecución |
|---|
| 1 | restricted | Bloqueado | Bloqueado | Bloqueado |
| 2 | permissive | Permitido | Permitido | Permitido |
| 3 | webserver | Permitido | Puertos 80, 443, 8080 | Bloqueado |
| 4 | database | Permitido | Puertos 5432, 6379 | Bloqueado |
| 5 | isolated | Permitido | Bloqueado | Bloqueado |
| 6 | web_with_db | Permitido | Puertos 80, 443, 5432, 3306, 6379 | Bloqueado |
| 7 | worker | Permitido | Puertos 443, 5432, 6379, 5672 | Permitido |
| Constante | Valor | Descripción |
|---|
PROTO_TCP | 6 | Protocolo TCP |
PROTO_UDP | 17 | Protocolo UDP |
DIR_BIND | 0 | socket bind() |
DIR_CONNECT | 1 | socket connect() |
| Aspecto | Modo Demonio | Modo Sin Demonio |
|---|
| Superficie de ataque | Demonio en ejecución | Sin proceso en ejecución |
| Inscripción | Socket Unix + alternativas | Solo alternativas |
| Actualizaciones de políticas | Recarga en caliente | Requiere reinicio |
| Registro de auditoría | Demonio lee ringbuf | journald mediante buffer perf |
| Eliminación de programas | Detener demonio | Solo reinicio |
| Orden de arranque | Después de network.target | Antes de basic.target |