警告:该项目正处于密集开发阶段,尚未准备好用于生产环境。API、策略格式及行为可能随时更改,恕不另行通知。仅供测试和实验使用。
注意:这是一个独立的实现,与 Meta 的解决方案并非同一项目。虽然 BpfJailer 在功能上类似,并且受到 Meta 的 Liam Wisehart、Justin Nga、Carl El Khoury、Mansee Chadha 原创想法和设计的启发,但这是独立开发的独立代码库。我们感谢激发此项工作的愿景和基础概念。
Jailer 是一个基于 eBPF 的进程隔离系统,为 Linux 提供强制访问控制(MAC)。它使用 BPF task_storage 映射跟踪进程,并对文件访问、网络操作和进程执行实施基于角色的策略。
| 功能 | 状态 | 描述 |
|---|---|---|
| 进程跟踪 | ✅ 可用 | 使用 task_storage BPF 映射跟踪进程 |
| Socket 注册 | ✅ 可用 | 进程通过 Unix socket API 注册 |
| 基于角色的策略 | ✅ 可用 | 受限角色和允许角色 |
| 文件访问控制 | ✅ 可用 | 阻止/允许文件打开操作 |
| Jail 继承 | ✅ 可用 | 子进程继承父进程的 jail |
| 网络控制 | ✅ 可用 | 阻止/允许 socket 绑定/连接 |
| 端口/协议过滤 | ✅ 可用 | 按端口 TCP/UDP 允许/拒绝规则 |
| 执行控制 | ✅ 可用 | 阻止/允许进程执行 |
| 路径匹配 | ✅ 可用 | 带缓存失效的目录项遍历 |
| 签名二进制文件 | 🚧 占位 | 二进制签名验证(未实现) |
| 备选注册 | ✅ 可用 | 按可执行文件、cgroup 或 xattr 自动注册 |
| 无守护进程模式 | ✅ 可用 | 引导二进制文件在早期启动时固定程序 |
| 审计事件 | ✅ 可用 | 用于 systemd-journald 集成的 perf 缓冲区 |

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)
# 安装 Rust 的 BPF 目标
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
# 在没有 jail 的情况下运行漏洞测试(显示攻击成功)
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
# 连接到守护进程
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect("/run/bpfjailer/enrollment.sock")
# 使用受限角色(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:
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 映射:
键:{ role_id, port, protocol, direction }
值:允许 (1) 或拒绝 (0)
// 在守护进程代码中:
use bpfjailer_daemon::process_tracker::{PROTO_TCP, DIR_CONNECT};
// 仅允许 TCP 连接到端口 80 和 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)?;
端口范围可以在 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 端口的范围,会记录警告。
┌─────────────────────────────────────────────────────────────┐
│ 用户空间 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ 客户端 │───▶│ bpfjailer-daemon │ │
│ │ (注册) │ │ │ │
│ └─────────────┘ │ - PolicyManager │ │
│ │ - ProcessTracker│ │
│ │ - EnrollmentSvr │ │
│ └────────┬─────────┘ │
│ │ 写入 │
│ ▼ │
├─────────────────────────────────────────────────────────────┤
│ BPF 映射 │
│ ┌──────────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │pending_enrollments│ │ role_flags │ │ task_storage │ │
│ │ (PID → 信息) │ │ (角色→标志) │ │(任务→信息) │ │
│ └──────────────────┘ └─────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ BPF LSM 钩子 │
│ ┌────────────┐ ┌───────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ task_alloc │ │ file_open │ │socket_bind/ │ │bprm_check│ │
│ │(继承 + 初始│ │(迁移 + 检 │ │ connect │ │_security │ │
│ │化) │ │查) │ │ (检查) │ │ (检查) │ │
│ └────────────┘ └───────────┘ └─────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
/run/bpfjailer/enrollment.sock{"Enroll": {"pod_id": N, "role_id": M}}pending_enrollments[PID] 和 role_flags[role_id]task_storagetask_storage + role_flags 以实施策略task_alloc 钩子继承BpfJailer 支持两种安装模式:
使用运行中的守护进程进行注册和策略管理:
# 安装 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
# 检查 socket 权限
ls -la /run/bpfjailer/enrollment.sock
除了 Unix socket 注册,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
在执行时(bprm_check_security LSM 钩子):
exec_enrollment 映射中cgroup_enrollment 映射中task_storage,包含 pod_id 和 role_id注册通过 fork/exec 保持(通过 task_alloc 钩子)
策略规则(网络、路径、执行)基于 role_id 应用
GPL-2.0(BPF 程序必需)
| 测试 | 漏洞 | 缓解方式 |
|---|
path_traversal | 通过 ../ 读取任意文件 | restricted, isolated |
command_injection | Shell 命令执行 | 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 绑定/连接 |
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 socket + 备选 | 仅备选 |
| 策略更新 | 热重载 | 需要重启 |
| 审计日志 | 守护进程读取 ringbuf | 通过 perf 缓冲区 journald |
| 程序移除 | 停止守护进程 | 仅重启 |
| 启动顺序 | network.target 之后 | basic.target 之前 |