自动化与脚本
在 Linux 上用 Rust/eBPF…
适用于 Linux 5.15+ 环境,需要用开源 eBPF + Rust 的 demo-runner 做网络监控/阻断、按 IP/域名 blocklist 拦截连接并消费内核事件。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Linux 上用 Rust/eBPF特定
解决思路
- 按架构理解组件:demo-runner 在用户态加载 eBPF 程序、填充 eBPF maps、读取内核事件;ebpf crate 在内核态用 TC、LSM 等程序拦截网络系统调用;common crate 提供内核与用户态共享类型。
- 按目录使用 crate:ebpf/ 放编译为 BPF bytecode 的内核态 eBPF 程序;common/ 放内核与用户态共享类型;demo-runner/ 作用户态加载器和事件消费者;webroot/ 放 JavaScript Web UI。
- 安装 stable Rust 工具链:rustup toolchain install stable。
- 安装 nightly 工具链并添加 rust-src(eBPF 编译需要):rustup toolchain install nightly --component rust-src。
- 安装 bpf-linker:cargo install bpf-linker。
- 安装 clang(eBPF 编译需要):Ubuntu/Debian 用 sudo apt install clang;Fedora/RHEL 用 sudo dnf install clang;Arch Linux 用 sudo pacman -S clang。
- 确认内核满足要求:Linux kernel 5.15+,内核配置启用 CONFIG_BPF=y 和 CONFIG_BPF_SYSCALL=y,并具备 CAP_BPF 或 root 权限来加载 eBPF 程序。
- 克隆仓库并进入目录:git clone https://github.com/obdev/littlesnitch-linux;cd littlesnitch-linux。
- 构建项目:cargo build --release;Cargo build scripts 会自动编译 eBPF 程序并嵌入二进制,无需手动编译 eBPF。
- 运行 demo runner:sudo cargo run --release(需要 root 或 CAP_BPF)。
- 仅做检查而不构建时运行:cargo check。
- 配置 blocked_hosts.txt:每行一个 IP 地址或 hostname。
- 配置 blocked_domains.txt:每行一个 domain suffix,会阻止该域名及其所有子域名。
- 运行前把两个 blocklist 文件放在工作目录,例如:echo "93.184.216.34" > blocked_hosts.txt;echo "example.com" > blocked_domains.txt;sudo cargo run --release。
- 扩展项目时把新的内核/用户态共享类型加到 common crate;示例共享类型包含 ConnectionEvent(pid、uid、src_addr、dst_addr、src_port、dst_port、protocol、action)和 IpKey(addr)。
- 编写内核 eBPF 程序时放在 ebpf/src/,使用 nightly 工具链编译为 BPF bytecode。
- TC eBPF 程序结构:使用 aya_ebpf::{macros::classifier, programs::TcContext, maps::HashMap},使用 TC_ACT_SHOT 和 TC_ACT_OK;用 #[map] 定义 BLOCKED_HOSTS: HashMap<IpKey, u8> = HashMap::with_max_entries(65536, 0)。
- 在 classifier egress_filter 中解析目的 IP,构造 IpKey,用 unsafe { BLOCKED_HOSTS.get(&key) } 查询;命中返回 TC_ACT_SHOT 丢包,否则返回 TC_ACT_OK。
- demo-runner 用 Aya 加载 eBPF 程序并操作 maps。
- 加载编译好的 eBPF object:Bpf::load(aya::include_loaded_bytes!("../../target/bpfel-unknown-none/release/ebpf"))。
- 附加 TC classifier 到网络接口:设置 iface,例如 "eth0";调用 tc::qdisc_add_clsact(iface);取 bpf.program_mut("egress_filter") 并 try_into 为 SchedClassifier;调用 program.load();调用 program.attach(iface, TcAttachType::Egress)。
- 从 blocked_hosts.txt 填充 BLOCKED_HOSTS map:读取文件,按行 trim,跳过空行和以 # 开头的行;解析 Ipv4Addr;用 u32::from(addr).to_be() 转为 key;blocked_hosts.insert(ip_u32, 1, 0);打印 Blocked host: {line}。
- 启动后打印 eBPF programs loaded. Monitoring traffic...,并用 tokio::signal::ctrl_c().await 等待 Ctrl+C;收到后打印 Shutting down. 并退出。
- 添加新 blocked domain:在 demo-runner 中写 load_blocked_domains,取 BLOCKED_DOMAINS map 为 HashMap<_, [u8; 256], u8>,读取文件,按行 trim,跳过空行;构造 [0u8; 256] key,把 domain bytes 复制进去;map.insert(key, 1, 0)。
- 从内核读取连接事件:取 EVENTS map 为 RingBuf::try_from(bpf.map_mut("EVENTS"));用 AsyncFd::new(ring_buf);循环 async_fd.readable_mut().await;用 ring_buf.next() 取 item;把 item 指针转为 &ConnectionEvent;打印 pid、dst、dst_port、action(action == 1 显示 BLOCKED,否则 ALLOWED);最后 guard.clear_ready()。
- 配置 demo-runner/Cargo.toml:依赖 aya = { version = "0.12", features = ["async_tokio"] }、aya-log = "0.2"、common = { path = "../common" }、anyhow = "1"、tokio = { version = "1", features = ["full"] }、log = "0.4"、env_logger = "0.10";build-dependencies 用 aya-build = "0.1"。
- 配置 ebpf/Cargo.toml:依赖 aya-ebpf = "0.1"、aya-log-ebpf = "0.1"、common = { path = "../common" };[[bin]] name = "ebpf",path = "src/main.rs"。
- 遇到 Operation not permitted 时:用 sudo cargo run --release;或在构建后执行 sudo setcap cap_bpf,cap_net_admin+eip target/release/demo-runner,然后 ./target/release/demo-runner。
- 遇到 bpf-linker not found 时:运行 cargo install bpf-linker;若失败,确保安装 LLVM:Debian/Ubuntu 用 sudo apt install llvm-dev libclang-dev。
- 遇到 eBPF verifier rejects program 时:减小 map 大小或循环边界;确保所有内存访问都有边界检查;检查内核是否支持所用 helpers,用 uname -r 确认应为 5.15+。
- 遇到 Map not found 时:运行 cargo build --release 2>&1 | grep -i ebpf 验证 eBPF object 是否构建并嵌入;demo-runner/build.rs 会自动处理该嵌入。
- 遇到 blocked_hosts.txt not found 时:从 repo root 运行,或显式指定路径;也可 touch blocked_hosts.txt blocked_domains.txt 后 sudo cargo run --release。
- 注意许可证:仓库内代码使用 GPL-2.0,向该项目提交的贡献也按相同条款许可。
适用边界 · 注意事项
- 不适用于非 Linux 环境,或内核低于 5.15、未启用 BTF/CO-RE/eBPF 的环境。
- 不适用于没有 root 或 CAP_BPF 权限、无法安装 Rust nightly、bpf-linker、clang 的环境。
- 不适用于需要完整 Little Snitch 专有 UI 和规则引擎,而非仅使用开源 eBPF 程序、共享类型和 demo runner 的场景。