
Jailer — это система изоляции процессов на основе eBPF, обеспечивающая мандатный контроль доступа (MAC) для Linux. Она отслеживает процессы с помощью BPF-карт task_storage и применяет ролевые политики к доступу к файлам, сетевым операциям и выполнению процессов.
Предупреждение: Этот проект находится в активной разработке и НЕ готов к использованию в production. API, форматы политик и поведение могут меняться без уведомления. Используйте только для тестирования и экспериментов.
Примечание: Это независимая реализация и не является тем же проектом, что и решение Meta. Хотя BpfJailer функционально схож и вдохновлён оригинальной идеей и дизайном Лайама Уайзхарта, Джастина Нга, Карла Эль Хури, Манси Чадха из Meta, это отдельная кодовая база, разработанная независимо. Мы выражаем благодарность за видение и основополагающие концепции, вдохновившие эту работу.
Jailer — это система изоляции процессов на основе eBPF, предоставляющая обязательный контроль доступа (MAC) для Linux. Она отслеживает процессы с помощью BPF-карт task_storage и обеспечивает применение политик на основе ролей для доступа к файлам, сетевых операций и выполнения процессов.

BPF_MAP_TYPE_TASK_STORAGE)# Проверка текущей конфигурации ядра
zcat /proc/config.gz 2>/dev/null || cat /boot/config-$(uname -r)
Необходимые параметры:
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_LSM=y
CONFIG_DEBUG_INFO_BTF=y
BPF LSM должен быть включен в параметрах загрузки ядра:
# Проверка активности BPF LSM
cat /sys/kernel/security/lsm
# В списке должен присутствовать "bpf"
# Если нет, добавьте в параметры загрузки ядра:
# Отредактируйте /etc/default/grub и добавьте в GRUB_CMDLINE_LINUX:
# lsm=lockdown,capability,landlock,yama,apparmor,bpf
# Затем обновите grub и перезагрузитесь:
sudo update-grub
sudo reboot
Для систем Ubuntu/Debian также можно использовать:
# Создайте скрипт для включения 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 включён. Пожалуйста, перезагрузитесь."
EOF
chmod +x /tmp/enable_bpf_lsm.sh
sudo /tmp/enable_bpf_lsm.sh
# Установка Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Установка зависимостей сборки (Ubuntu/Debian)
sudo apt-get install -y clang llvm libelf-dev linux-headers-$(uname -r)
# Установка цели BPF для Rust
rustup target add bpfel-unknown-none
cd bpfjail
# Сборка BPF-программ
cd bpfjailer-bpf && cargo build --release && cd ..
# Сборка демона и клиента
cargo build --release
# Запуск от root (загружает config/policy.json, если присутствует)
sudo RUST_LOG=info ./target/release/bpfjailer-daemon
Ожидаемый вывод:
[INFO] BpfJailer daemon starting...
[INFO] Loading BpfJailer eBPF programs with libbpf-rs...
[INFO] ✓ pending_enrollments map available for enrollment
[INFO] ✓ network_rules map available for port/protocol filtering
[INFO] ✓ task_storage map created successfully
[INFO] ✓ Program task_alloc attached
[INFO] ✓ Program file_open attached
[INFO] ✓ Program socket_bind attached
[INFO] ✓ Program socket_connect attached
[INFO] ✓ Program bprm_check_security attached
[INFO] Initialized with default roles: restricted (1), permissive (2)
[INFO] Loaded policy from config/policy.json
[INFO] Loaded 5 roles
[INFO] Enrollment server listening on /run/bpfjailer/enrollment.sock
# Запуск тестов уязвимостей БЕЗ изоляции (показывает успешные атаки)
sudo python3 tests/vulnerable_apps/run_tests.py
# Запуск тестов уязвимостей С ограниченной ролью (показывает заблокированные атаки)
sudo python3 tests/vulnerable_apps/run_tests.py --role 1
# Запуск конкретного теста
sudo python3 tests/vulnerable_apps/run_tests.py --role 1 --test path
# Список доступных тестов и ролей
sudo python3 tests/vulnerable_apps/run_tests.py --list
Пример вывода с ограниченной ролью:
============================================================
TEST: Path Traversal / Arbitrary File Read
============================================================
Attempting to read /etc/passwd via path traversal...
BLOCKED - Permission denied (BpfJailer blocked file access)
============================================================
TEST: Command Injection
============================================================
Attempting command injection...
BLOCKED - Permission denied (BpfJailer blocked exec)
============================================================
TEST: Reverse Shell / Data Exfiltration
============================================================
Test 1: Reverse shell connection to 127.0.0.1:4444
BLOCKED - Permission denied (BpfJailer blocked connect)
#!/usr/bin/env python3
import socket
import json
import os
# Connect to daemon
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect("/run/bpfjailer/enrollment.sock")
# Enroll with restricted role (ID 1)
request = {"Enroll": {"pod_id": 1, "role_id": 1}}
sock.send((json.dumps(request) + "\n").encode())
response = sock.recv(4096).decode()
print(f"Enrollment: {response}")
sock.close()
# Try to read a file (should be blocked)
try:
open("/etc/passwd").read()
print("File access: ALLOWED")
except PermissionError:
print("File access: BLOCKED")
BpfJailer загружает роли из JSON-файла политик. Демон ищет файл политик в следующем порядке:
$BPFJAILER_POLICY/etc/bpfjailer/policy.jsonconfig/policy.json (относительно рабочего каталога){
"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 поддерживает тонкую настройку сетевого контроля с правилами TCP/UDP для каждого порта.
network_rules map:
Key: { role_id, port, protocol, direction }
Value: разрешён (1) или запрещён (0)
// In daemon code:
use bpfjailer_daemon::process_tracker::{PROTO_TCP, DIR_CONNECT};
// Allow TCP connect to ports 80 and 443 only
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)?;
Диапазоны портов можно указать в policy.json с помощью port_start и port_end:
{
"network_rules": [
{"protocol": "tcp", "port": 443, "allow": true},
{"protocol": "tcp", "port_start": 8000, "port_end": 8100, "allow": true}
]
}
| Поле | Описание |
|---|---|
port | Один порт (например, 80) |
port_start + port_end | Диапазон портов (например, 8000-8100) |
Примечание: Большие диапазоны (>1000 портов) потребляют много записей BPF-карты. Для диапазонов, превышающих 1000 портов, записывается предупреждение.
┌─────────────────────────────────────────────────────────────┐
│ User Space │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ Client │───▶│ bpfjailer-daemon │ │
│ │ (enroll) │ │ │ │
│ └─────────────┘ │ - PolicyManager │ │
│ │ - ProcessTracker│ │
│ │ - EnrollmentSvr │ │
│ └────────┬─────────┘ │
│ │ writes to │
│ ▼ │
├─────────────────────────────────────────────────────────────┤
│ BPF Maps │
│ ┌──────────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │pending_enrollments│ │ role_flags │ │ task_storage │ │
│ │ (PID → info) │ │ (role→flags)│ │(task→info) │ │
│ └──────────────────┘ └─────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ BPF LSM Hooks │
│ ┌────────────┐ ┌───────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ task_alloc │ │ file_open │ │socket_bind/ │ │bprm_check│ │
│ │(inheritance│ │(migration │ │ connect │ │_security │ │
│ │ + init) │ │ + check) │ │ (check) │ │ (check) │ │
│ └────────────┘ └───────────┘ └─────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
/run/bpfjailer/enrollment.sock{"Enroll": {"pod_id": N, "role_id": M}}pending_enrollments[PID] и role_flags[role_id]task_storagetask_storage + role_flags для принудительного примененияtask_allocBpfJailer поддерживает два режима установки:
Использует работающий демон для регистрации и управления политиками:
# Установка 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/
# Включение и запуск
sudo systemctl daemon-reload
sudo systemctl enable bpfjailer-daemon
sudo systemctl start bpfjailer-daemon
Возможности:
Загружает BPF-программы при ранней загрузке и завершает работу. Программы остаются активными до перезагрузки:
# Установка загрузочного сервиса
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/
# Включение (будет запущен при следующей загрузке)
sudo systemctl daemon-reload
sudo systemctl enable bpfjailer-bootstrap
# Или запустить вручную сейчас
sudo bpfjailer-bootstrap
Возможности:
/sys/fs/bpf/bpfjailer/Проверка закрепленных программ:
ls -la /sys/fs/bpf/bpfjailer/
ls -la /sys/fs/bpf/bpfjailer/maps/
ls -la /sys/fs/bpf/bpfjailer/progs/
Просмотр событий аудита:
# События отправляются в perf-буфер, захватываются journald
journalctl -f | grep bpfjailer
# Проверка версии ядра (требуется 5.11+)
uname -r
# Проверка активности BPF LSM
cat /sys/kernel/security/lsm | grep bpf
# Проверка доступности BTF
ls -la /sys/kernel/btf/vmlinux
# Проверка, могут ли BPF-программы быть загружены
sudo bpftool prog list
# Проверка возможностей
sudo capsh --print | grep cap_bpf
# Убедитесь, что демон запущен от root
ps aux | grep bpfjailer
# Проверка прав на сокет
ls -la /run/bpfjailer/enrollment.sock
Помимо регистрации через Unix-сокет, BpfJailer поддерживает автоматическую регистрацию на основе:
Автоматическая регистрация всех процессов, выполняющих конкретный бинарный файл:
{
"exec_enrollments": [
{
"executable_path": "/usr/bin/nginx",
"pod_id": 1000,
"role": "webserver"
}
]
}
Когда любой процесс выполняет /usr/bin/nginx, он автоматически регистрируется с ролью webserver. Сопоставление выполняется по inode исполняемого файла, поэтому символические и жёсткие ссылки обрабатываются корректно.
Автоматическая регистрация всех процессов в определённой cgroup:
{
"cgroup_enrollments": [
{
"cgroup_path": "/sys/fs/cgroup/bpfjailer/sandbox",
"pod_id": 2000,
"role": "sandbox"
}
]
}
Создайте cgroup и переместите в неё процессы:
# Создание cgroup
sudo mkdir -p /sys/fs/cgroup/bpfjailer/sandbox
# Перемещение процесса в cgroup
echo $$ | sudo tee /sys/fs/cgroup/bpfjailer/sandbox/cgroup.procs
# Процесс теперь автоматически зарегистрирован с ролью sandbox
Установите расширенные атрибуты на исполняемые файлы для информации о регистрации:
# Установка xattr для регистрации
sudo setfattr -n user.bpfjailer.pod_id -v $(printf '\x01\x00\x00\x00\x00\x00\x00\x00') /path/to/binary
sudo setfattr -n user.bpfjailer.role_id -v $(printf '\x03\x00\x00\x00') /path/to/binary
# Проверка xattr
getfattr -d /path/to/binary
Во время выполнения (хук LSM bprm_check_security):
exec_enrollmentcgroup_enrollmenttask_storage с pod_id и role_idРегистрация сохраняется при fork/exec через хук task_alloc
Правила политик (сеть, пути, выполнение) применяются на основе role_id
GPL-2.0 (требуется для BPF-программ)
| Функция | Статус | Описание |
|---|
| Отслеживание процессов | ✅ Работает | Отслеживает процессы с помощью BPF-карты task_storage |
| Регистрация через сокет | ✅ Работает | Процессы регистрируются через API Unix-сокета |
| Политики на основе ролей | ✅ Работает | Ограниченные и разрешительные роли |
| Контроль доступа к файлам | ✅ Работает | Блокировка/разрешение операций открытия файлов |
| Наследование изоляции | ✅ Работает | Дочерние процессы наследуют изоляцию родителя |
| Сетевой контроль | ✅ Работает | Блокировка/разрешение socket bind/connect |
| Фильтрация портов/протоколов | ✅ Работает | Правила разрешения/запрета TCP/UDP по портам |
| Контроль выполнения | ✅ Работает | Блокировка/разрешение выполнения процессов |
| Сопоставление путей | ✅ Работает | Обход dentry с инвалидацией кэша |
| Подписанные бинарные файлы | 🚧 Заглушка | Проверка подписи бинарных файлов (не реализовано) |
| Альтернативная регистрация | ✅ Работает | Авторегистрация по исполняемому файлу, cgroup или xattr |
| Режим без демона | ✅ Работает | Загрузочный бинарный файл закрепляет программы при ранней загрузке |
| События аудита | ✅ Работает | Perf-буфер для интеграции с systemd-journald |
| Тест | Уязвимость | Смягчается ролью |
|---|
path_traversal | Чтение произвольных файлов через ../ | restricted, isolated |
command_injection | Выполнение команд оболочки | restricted, webserver, isolated |
reverse_shell | Исходящие соединения к атакующему | restricted, isolated |
ssrf | Доступ к внутренним сервисам/метаданным облака | restricted, isolated |
arbitrary_write | Запись в критически важные пути | restricted |
crypto_miner | Скачивание + выполнение + соединение с пулом | restricted, webserver |
privilege_escalation | Чтение shadow, запись в sudoers | restricted |
| Флаг | Описание |
|---|
allow_file_access | Разрешить операции открытия файлов |
allow_network | Разрешить socket bind/connect |
allow_exec | Разрешить выполнение процессов |
allow_setuid | Разрешить операции setuid |
allow_ptrace | Разрешить операции ptrace |
| ID роли | Имя | Доступ к файлам | Сеть | Выполнение |
|---|
| 1 | restricted | Заблокирован | Заблокирована | Заблокировано |
| 2 | permissive | Разрешён | Разрешена | Разрешено |
| 3 | webserver | Разрешён | Порты 80, 443, 8080 | Заблокировано |
| 4 | database | Разрешён | Порты 5432, 6379 | Заблокировано |
| 5 | isolated | Разрешён | Заблокирована | Заблокировано |
| 6 | web_with_db | Разрешён | Порты 80, 443, 5432, 3306, 6379 | Заблокировано |
| 7 | worker | Разрешён | Порты 443, 5432, 6379, 5672 | Разрешено |
| Константа | Значение | Описание |
|---|
PROTO_TCP | 6 | Протокол TCP |
PROTO_UDP | 17 | Протокол UDP |
DIR_BIND | 0 | socket bind() |
DIR_CONNECT | 1 | socket connect() |
| Аспект | Режим демона | Режим без демона |
|---|
| Поверхность атаки | Работающий демон | Нет работающего процесса |
| Регистрация | Unix-сокет + альтернативы | Только альтернативы |
| Обновление политик | Горячая перезагрузка | Требуется перезагрузка |
| Логирование аудита | Демон читает ringbuf | journald через perf-буфер |
| Удаление программ | Остановка демона | Только перезагрузка |
| Порядок загрузки | После network.target | До basic.target |