gen0sec/jailer

GitHub: gen0sec/jailer

基于 eBPF LSM 的 Linux 进程强制访问控制系统,通过角色策略在内核层面限制进程的文件访问、网络连接和执行权限。

Stars: 42 | Forks: 2

# Jailer - eBPF 强制访问控制 ## 功能特性(当前版本) | Feature | Status | Description | |---------|--------|-------------| | 进程追踪 | ✅ 正常 | 使用 `task_storage` BPF map 追踪进程 | | Socket 注册 | ✅ 正常 | 进程通过 Unix socket API 进行注册 | | 基于角色的策略 | ✅ 正常 | 受限 (restricted) 和 宽松 (permissive) 角色 | | 文件访问控制 | ✅ 正常 | 阻止/允许文件打开操作 | | Jail 继承 | ✅ 正常 | 子进程继承父进程的 jail | | 网络控制 | ✅ 正常 | 阻止/允许 socket bind/connect | | 端口/协议过滤 | ✅ 正常 | 基于端口的 TCP/UDP 允许/拒绝规则 | | 执行控制 | ✅ 正常 | 阻止/允许进程执行 | | 路径匹配 | ✅ 正常 | 带缓存失效机制的 Dentry 遍历 | | 签名二进制文件 | 🚧 桩 | 二进制签名验证(未实现) | | 替代注册方式 | ✅ 正常 | 按可执行文件、cgroup 或 xattr 自动注册 | | 无守护进程模式 | ✅ 正常 | 引导二进制文件在启动早期 pin 程序 | | 审计事件 | ✅ 正常 | 用于 systemd-journald 集成的 Perf buffer | ## Nginx 演示 ![Nginx 演示](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/3762ef8be9230209.gif) ## 复杂场景演示 [![asciicast](https://asciinema.org/a/767420.svg)](https://asciinema.org/a/767420) ## 内核要求 ### 最低内核版本 - **Linux 5.11+**(用于支持 `BPF_MAP_TYPE_TASK_STORAGE`) - **推荐:Linux 6.1+**(更好的 BTF 支持) ### 必需的内核配置 ``` # 检查当前内核配置 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: ``` # 检查 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 enabled. Please reboot." 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 target rustup target add bpfel-unknown-none ``` ### 构建所有组件 ``` cd bpfjail # 构建 BPF programs cd bpfjailer-bpf && cargo build --release && cd .. # 构建 daemon 和 client cargo build --release ``` ## 快速开始 ### 1. 启动 Daemon ``` # 以 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 ``` ### 2. 运行安全测试 ``` # 运行漏洞测试且不使用 jailing (显示攻击成功) sudo python3 tests/vulnerable_apps/run_tests.py # 运行漏洞测试且使用 restricted role (显示攻击被阻止) sudo python3 tests/vulnerable_apps/run_tests.py --role 1 # 运行特定测试 sudo python3 tests/vulnerable_apps/run_tests.py --role 1 --test path # 列出可用测试和 roles 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) ``` ### 可用的安全测试 | Test | Vulnerability | Mitigated By | |------|--------------|--------------| | `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 | ### 3. 手动注册测试 ``` #!/usr/bin/env python3 import socket import json import os # 连接到 daemon sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.connect("/run/bpfjailer/enrollment.sock") # 使用 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: open("/etc/passwd").read() print("File access: ALLOWED") except PermissionError: print("File access: BLOCKED") ``` ## 策略文件 BpfJailer 从 JSON 策略文件加载角色。守护进程按以下顺序搜索策略文件: 1. `$BPFJAILER_POLICY` 环境变量 2. `/etc/bpfjailer/policy.json` 3. `config/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": [] } ``` ### 策略标志位 | Flag | Description | |------|-------------| | `allow_file_access` | 允许文件打开操作 | | `allow_network` | 允许 socket bind/connect | | `allow_exec` | 允许进程执行 | | `allow_setuid` | 允许 setuid 操作 | | `allow_ptrace` | 允许 ptrace 操作 | ## 默认角色 | Role ID | Name | File Access | Network | Exec | |---------|------|-------------|---------|------| | 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 | 已允许 | ## 网络端口/协议过滤 BpfJailer 支持基于端口的 TCP/UDP 规则,实现细粒度网络控制。 ### 规则结构 ``` network_rules map: Key: { role_id, port, protocol, direction } Value: allowed (1) or denied (0) ``` - **protocol**: 6 = TCP, 17 = UDP - **direction**: 0 = bind, 1 = connect - **port**: 0 = 通配符(所有端口) ### 规则评估顺序 1. 检查该角色的特定端口规则 2. 检查该角色的通配符端口规则 (port=0) 3. 回退到 role_flags(位 1 = 允许网络) ### 示例:为受限角色仅允许 HTTP/HTTPS ``` // 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)?; ``` ### 协议常量 | Constant | Value | Description | |----------|-------|-------------| | `PROTO_TCP` | 6 | TCP 协议 | | `PROTO_UDP` | 17 | UDP 协议 | | `DIR_BIND` | 0 | socket bind() | | `DIR_CONNECT` | 1 | socket connect() | ### 端口范围 可以在 policy.json 中使用 `port_start` 和 `port_end` 指定端口范围: ``` { "network_rules": [ {"protocol": "tcp", "port": 443, "allow": true}, {"protocol": "tcp", "port_start": 8000, "port_end": 8100, "allow": true} ] } ``` | Field | Description | |-------|-------------| | `port` | 单个端口(例如 80) | | `port_start` + `port_end` | 端口范围(例如 8000-8100) | ## 架构 ``` ┌─────────────────────────────────────────────────────────────┐ │ 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) │ │ │ └────────────┘ └───────────┘ └─────────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ## 注册流程 1. 进程连接到 `/run/bpfjailer/enrollment.sock` 2. 发送 JSON:`{"Enroll": {"pod_id": N, "role_id": M}}` 3. 守护进程写入 `pending_enrollments[PID]` 和 `role_flags[role_id]` 4. 在下一次系统调用(file_open, exec)时,BPF 将注册迁移到 `task_storage` 5. 所有未来的系统调用检查 `task_storage` + `role_flags` 以进行强制执行 6. 子进程通过 `task_alloc` 钩子继承 ## 安装模式 BpfJailer 支持两种安装模式: ### 1. Daemon 模式(标准) 使用运行中的守护进程进行注册和策略管理: ``` # 安装 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 ``` **特性:** - 基于 Socket 的注册 API - 策略热加载(停止/启动守护进程) - 守护进程中的完整日志记录 ### 2. 无守护进程模式 在启动早期加载 BPF 程序并退出。程序保持活动状态直到重启: ``` # 安装 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/ # 启用 (将在下次启动时运行) sudo systemctl daemon-reload sudo systemctl enable bpfjailer-bootstrap # 或者立即手动运行 sudo bpfjailer-bootstrap ``` **特性:** - 无运行中的守护进程(减少攻击面) - BPF 程序被 pin 到 `/sys/fs/bpf/bpfjailer/` - 不重启无法停止 - 审计事件通过 perf buffer 发送到 systemd-journald - 仅替代注册方法有效 **检查 pinned 程序:** ``` ls -la /sys/fs/bpf/bpfjailer/ ls -la /sys/fs/bpf/bpfjailer/maps/ ls -la /sys/fs/bpf/bpfjailer/progs/ ``` **查看审计事件:** ``` # 事件发送到 perf buffer,由 journald 收集 journalctl -f | grep bpfjailer ``` ### 模式对比 | Aspect | Daemon Mode | Daemonless Mode | |--------|-------------|-----------------| | 攻击面 | 运行中的守护进程 | 无运行中的进程 | | 注册方式 | Unix socket + 替代方案 | 仅替代方案 | | 策略更新 | 热加载 | 需要重启 | | 审计日志 | 守护进程读取 ringbuf | 通过 perf buffer 发送至 journald | | 程序移除 | 停止守护进程 | 仅限重启 | | 启动顺序 | network.target 之后 | basic.target 之前 | ## 故障排除 ### "task_storage map creation failed" ``` # 检查内核版本 (需要 5.11+) uname -r # 检查 BPF LSM 是否处于活动状态 cat /sys/kernel/security/lsm | grep bpf # 检查 BTF 是否可用 ls -la /sys/kernel/btf/vmlinux ``` ### "Failed to attach program" ``` # 检查 BPF programs 是否可加载 sudo bpftool prog list # 检查 capabilities sudo capsh --print | grep cap_bpf ``` ### 注册时权限被拒绝 ``` # 确保 daemon 正以 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 中的所有进程: ``` { "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 role 自动注册 ``` ### 基于 Xattr 的注册 在可执行文件上设置扩展属性以存储注册信息: ``` # 设置注册 xattrs 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 # 检查 xattrs getfattr -d /path/to/binary ``` ### 自动注册的工作原理 1. **在 exec 时**(`bprm_check_security` LSM 钩子): - 检查可执行文件的 inode 是否在 `exec_enrollment` map 中 - 检查进程的 cgroup ID 是否在 `cgroup_enrollment` map 中 - 如果找到,自动设置 `task_storage` 中的 pod_id 和 role_id 2. **注册通过** `task_alloc` 钩子在 fork/exec 过程中**持久保留** 3. **策略规则**(网络、路径、执行)根据 role_id 应用 ## 许可证 GPL-2.0(BPF 程序必需)
标签:0day挖掘, Cloud Native, Docker镜像, Jailer, LSM, MAC, RBAC, Role-Based Access Control, Web截图, 入侵防御, 内核安全, 可视化界面, 子域名枚举, 客户端加密, 容器安全, 强制访问控制, 文件访问控制, 权限控制, 系统安全, 网络安全, 进程沙箱, 隐私保护