657 字
2 分钟
综合实战:构建 eBPF 网络安全工具
前 17 章从 eBPF 虚拟机、验证器、Map 数据结构、Hook 点、CO-RE,一路走到 XDP、TC、LSM、Cilium、生产部署。现在到了综合运用的时候——从零构建一个三层 eBPF 网络安全工具。
一、项目概述
1.1 三层安全架构
graph TB
subgraph "三层安全工具"
L1["Layer 1: XDP<br/>防 DDoS<br/>最早拦截点"]
L2["Layer 2: TC<br/>流量控制<br/>可访问 skb"]
L3["Layer 3: LSM<br/>进程监控<br/>安全检查点"]
end
NET["网络数据包"] --> L1
L1 -->|"XDP_PASS"| L2
L2 -->|"进入应用"| APP["应用程序"]
APP -->|"系统调用"| L3
CTRL["Go 控制平面<br/>规则管理 + 指标导出"]
CTRL -->|"更新 Map"| L1 & L2 & L3
style L1 fill:#ffcdd2,stroke:#c62828
style L2 fill:#fff9c4,stroke:#f9a825
style L3 fill:#e1bee7,stroke:#6a1b9a
1.2 功能列表
| 层级 | 功能 | Hook 点 | 详见 |
|---|---|---|---|
| XDP | IP 黑名单、速率限制、SYN Flood 防护 | XDP 入口 | Ch7 XDP |
| TC | 带宽限制、连接跟踪、端口过滤 | TC ingress/egress | Ch8 TC |
| LSM | 进程执行监控、文件访问控制、提权检测 | LSM BPF | Ch10 eBPF安全 |
二、Layer 1:XDP 防 DDoS
2.1 XDP 程序
#include <linux/bpf.h>#include <bpf/bpf_helpers.h>#include <linux/if_ether.h>#include <linux/ip.h>#include <linux/tcp.h>
// Map:IP 黑名单struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10000); __type(key, __u32); // IP 地址 __type(value, __u32); // 0=黑名单} blacklist SEC(".maps");
// Map:速率限制计数器struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, 10000); __type(key, __u32); // IP 地址 __type(value, struct rate_counter);} rate_limit SEC(".maps");
struct rate_counter { __u64 packets; // 包计数 __u64 last_reset; // 上次重置时间};
// Map:统计指标struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(max_entries, 4); __type(key, __u32); __type(value, __u64);} stats SEC(".maps");
#define STAT_TOTAL 0#define STAT_BLOCKED 1#define STAT_PASSED 2#define STAT_RATE 3
#define MAX_PPS 1000 // 每秒最大包数
SEC("xdp")int xdp_security(struct xdp_md *ctx) { void *data = (void *)(long)ctx->data; void *data_end = (void *)(long)ctx->data_end;
// 解析以太网头 struct ethhdr *eth = data; if ((void *)(eth + 1) > data_end) return XDP_PASS; if (eth->h_proto != bpf_htons(ETH_P_IP)) return XDP_PASS;
// 解析 IP 头 struct iphdr *ip = (void *)(eth + 1); if ((void *)(ip + 1) > data_end) return XDP_PASS;
__u32 src_ip = ip->saddr; __u32 idx = STAT_TOTAL; __u64 *val = bpf_map_lookup_elem(&stats, &idx); if (val) (*val)++;
// 1. 黑名单检查 __u32 *blocked = bpf_map_lookup_elem(&blacklist, &src_ip); if (blocked) { idx = STAT_BLOCKED; val = bpf_map_lookup_elem(&stats, &idx); if (val) (*val)++; return XDP_DROP; }
// 2. 速率限制 struct rate_counter *rc = bpf_map_lookup_elem(&rate_limit, &src_ip); if (!rc) { struct rate_counter new_rc = {1, bpf_ktime_get_ns()}; bpf_map_update_elem(&rate_limit, &src_ip, &new_rc, BPF_ANY); } else { __u64 now = bpf_ktime_get_ns(); __u64 elapsed = now - rc->last_reset; if (elapsed > 1000000000ULL) { // 1 秒 rc->packets = 1; rc->last_reset = now; } else { rc->packets++; if (rc->packets > MAX_PPS) { idx = STAT_RATE; val = bpf_map_lookup_elem(&stats, &idx); if (val) (*val)++; return XDP_DROP; } } }
// 3. SYN Flood 检测(简化) if (ip->protocol == IPPROTO_TCP) { struct tcphdr *tcp = (void *)(ip + 1); if ((void *)(tcp + 1) > data_end) return XDP_PASS; if (tcp->syn && !tcp->ack) { // SYN 包:可进一步检测 bpf_trace_printk("SYN from %x\\n", src_ip); } }
idx = STAT_PASSED; val = bpf_map_lookup_elem(&stats, &idx); if (val) (*val)++;
return XDP_PASS;}
char LICENSE[] SEC("license") = "GPL";2.2 编译和加载
# 编译 XDP 程序clang -g -O2 -target bpf -D__TARGET_ARCH_x86 \ -I/usr/include/$(uname -m)-linux-gnu \ -c xdp_security.bpf.c -o xdp_security.bpf.o
# 加载到网络接口sudo bpftool prog load xdp_security.bpf.o /sys/fs/bpf/xdp_security \ type xdp name xdp_security
# 挂载到接口sudo ip link set dev eth0 xdp pinned /sys/fs/bpf/xdp_security
# 添加黑名单 IPsudo bpftool map update pinned /sys/fs/bpf/xdp_security/blacklist \ key 4 0x0100007f value 4 0x00000000 # 127.0.0.1
# 查看统计sudo bpftool map dump pinned /sys/fs/bpf/xdp_security/stats三、Layer 2:TC 流量控制
3.1 TC 程序
#include <linux/bpf.h>#include <bpf/bpf_helpers.h>#include <linux/pkt_cls.h>
// Map:端口过滤规则struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 100); __type(key, __u16); // 端口号 __type(value, __u32); // 0=允许, 1=拒绝} port_filter SEC(".maps");
// Map:带宽限制struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 100); __type(key, __u32); // IP 地址 __type(value, struct bw_limit);} bandwidth SEC(".maps");
struct bw_limit { __u64 bytes_sent; __u64 last_reset; __u64 max_bytes; // 每秒最大字节};
SEC("tc")int tc_security(struct __sk_buff *skb) { void *data = (void *)(long)skb->data; void *data_end = (void *)(long)skb->data_end;
struct ethhdr *eth = data; if ((void *)(eth + 1) > data_end) return TC_ACT_OK; if (eth->h_proto != bpf_htons(ETH_P_IP)) return TC_ACT_OK;
struct iphdr *ip = (void *)(eth + 1); if ((void *)(ip + 1) > data_end) return TC_ACT_OK;
// 端口过滤 if (ip->protocol == IPPROTO_TCP) { struct tcphdr *tcp = (void *)(ip + 1); if ((void *)(tcp + 1) > data_end) return TC_ACT_OK;
__u16 dst_port = bpf_ntohs(tcp->dest); __u32 *action = bpf_map_lookup_elem(&port_filter, &dst_port); if (action && *action == 1) { return TC_ACT_SHOT; // 拒绝 } }
// 带宽限制 __u32 src_ip = ip->saddr; struct bw_limit *bw = bpf_map_lookup_elem(&bandwidth, &src_ip); if (bw) { __u64 now = bpf_ktime_get_ns(); __u64 elapsed = now - bw->last_reset; if (elapsed > 1000000000ULL) { bw->bytes_sent = skb->len; bw->last_reset = now; } else { bw->bytes_sent += skb->len; if (bw->bytes_sent > bw->max_bytes) { return TC_ACT_SHOT; // 超出带宽限制 } } }
return TC_ACT_OK;}
char LICENSE[] SEC("license") = "GPL";3.2 挂载 TC 程序
# 编译clang -g -O2 -target bpf -D__TARGET_ARCH_x86 \ -c tc_security.bpf.c -o tc_security.bpf.o
# 创建 qdiscsudo tc qdisc add dev eth0 clsact
# 挂载 ingresssudo tc filter add dev eth0 ingress bpf da obj tc_security.bpf.o sec tc
# 添加端口过滤规则(拒绝 23 端口 Telnet)sudo bpftool map update pinned /sys/fs/bpf/tc_security/port_filter \ key 2 0x0017 value 4 0x00000001四、Layer 3:LSM 进程监控
4.1 LSM 程序
#include <linux/bpf.h>#include <bpf/bpf_helpers.h>
// Map:监控规则struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 100); __type(key, __u32); // UID __type(value, __u32); // 0=监控, 1=阻止} exec_rules SEC(".maps");
// Map:事件日志struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024);} events SEC(".maps");
struct exec_event { __u32 uid; __u32 pid; char comm[16]; char filename[256];};
// Hook:进程执行SEC("lsm/bprm_check_security")int BPF_PROG(check_exec, struct linux_binprm *bprm) { __u32 uid = bpf_get_current_uid_gid() >> 32; __u32 pid = bpf_get_current_pid_tgid() >> 32;
// 检查规则 __u32 *rule = bpf_map_lookup_elem(&exec_rules, &uid); if (rule && *rule == 1) { // 阻止执行 struct exec_event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0); if (e) { e->uid = uid; e->pid = pid; bpf_get_current_comm(e->comm, sizeof(e->comm)); bpf_probe_read_kernel_str(e->filename, sizeof(e->filename), bprm->filename); bpf_ringbuf_submit(e, 0); } return -EPERM; // 阻止 }
// 记录事件 struct exec_event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0); if (e) { e->uid = uid; e->pid = pid; bpf_get_current_comm(e->comm, sizeof(e->comm)); bpf_probe_read_kernel_str(e->filename, sizeof(e->filename), bprm->filename); bpf_ringbuf_submit(e, 0); }
return 0; // 允许}
// Hook:文件打开(检测敏感文件访问)SEC("lsm/file_open")int BPF_PROG(check_file_open, struct file *file) { // 检测 /etc/shadow, /etc/passwd 等敏感文件访问 char filename[64]; bpf_probe_read_kernel_str(filename, sizeof(filename), file->f_path.dentry->d_name.name);
if (filename[0] == 's' && filename[1] == 'h' && filename[2] == 'a' && filename[3] == 'd') { // shadow 文件访问 bpf_trace_printk("sensitive file access: %s\\n", filename); }
return 0;}
char LICENSE[] SEC("license") = "GPL";Note
LSM BPF 需要 Linux 5.7+ 且启用 CONFIG_BPF_LSM。在生产环境中,LSM 程序需要经过严格的安全审查——错误的 LSM 规则可能阻止合法的系统操作。
五、Go 控制平面
5.1 规则管理
package main
import ( "fmt" "log" "net" "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/ringbuf")
type SecurityTool struct { xdpProg *ebpf.Program tcProg *ebpf.Program lsmProg *ebpf.Program blacklist *ebpf.Map portFilter *ebpf.Map execRules *ebpf.Map events *ebpf.Map}
func (t *SecurityTool) AddBlacklistIP(ip string) error { ipAddr := net.ParseIP(ip).To4() key := binary.BigEndian.Uint32(ipAddr) val := uint32(0) return t.blacklist.Update(key, val, ebpf.UpdateAny)}
func (t *SecurityTool) RemoveBlacklistIP(ip string) error { ipAddr := net.ParseIP(ip).To4() key := binary.BigEndian.Uint32(ipAddr) return t.blacklist.Delete(key)}
func (t *SecurityTool) BlockPort(port uint16) error { key := port val := uint32(1) return t.portFilter.Update(key, val, ebpf.UpdateAny)}
func (t *SecurityTool) BlockUIDExec(uid uint32) error { key := uid val := uint32(1) return t.execRules.Update(key, val, ebpf.UpdateAny)}
func (t *SecurityTool) ReadEvents() { reader, err := ringbuf.NewReader(t.events) if err != nil { log.Fatal(err) }
for { record, err := reader.Read() if err != nil { continue }
// 解析事件 var event ExecEvent binary.Read(bytes.NewReader(record.RawSample), binary.LittleEndian, &event)
fmt.Printf("[LSM] uid=%d pid=%d comm=%s file=%s\\n", event.Uid, event.Pid, event.Comm, event.Filename) }}5.2 指标导出
func (t *SecurityTool) ExportMetrics() { // 定期读取 stats Map ticker := time.NewTicker(5 * time.Second) for range ticker.C { var total, blocked, passed, rate uint64
t.stats.Lookup(uint32(0), &total) t.stats.Lookup(uint32(1), &blocked) t.stats.Lookup(uint32(2), &passed) t.stats.Lookup(uint32(3), &rate)
fmt.Printf("[XDP] total=%d blocked=%d passed=%d rate_limited=%d\\n", total, blocked, passed, rate) }}六、测试与部署
6.1 测试
# 测试 XDP 黑名单sudo bpftool map update pinned /sys/fs/bpf/blacklist \ key hex 0a000001 value hex 00000000 # 10.0.0.1
# 从 10.0.0.1 发送包 → 应被 DROPping -c 3 10.0.0.1 # 应不通
# 测试速率限制hping3 --flood -S -p 80 target_ip # SYN Flood# 超过 1000 PPS 的源 IP 应被限制
# 测试 LSM 进程监控sudo bpftool map update pinned /sys/fs/bpf/exec_rules \ key hex 000003e8 value hex 00000001 # UID 1000 被阻止
# UID 1000 尝试执行 → 应被阻止sudo -u testuser ls # 应返回 Permission denied6.2 部署
# Kubernetes DaemonSetapiVersion: apps/v1kind: DaemonSetmetadata: name: ebpf-securityspec: selector: matchLabels: app: ebpf-security template: spec: hostNetwork: true containers: - name: security-tool image: ebpf-security:latest securityContext: privileged: true volumeMounts: - name: bpf mountPath: /sys/fs/bpf - name: debug mountPath: /sys/kernel/debug volumes: - name: bpf hostPath: path: /sys/fs/bpf - name: debug hostPath: path: /sys/kernel/debug/tracing6.3 性能基准
| 层级 | 开销 | PPS 影响 | 说明 |
|---|---|---|---|
| XDP 黑名单 | ~0.5 μs/包 | < 1% | Hash 查找极快 |
| XDP 速率限制 | ~1 μs/包 | < 2% | LRU Hash 查找 |
| TC 端口过滤 | ~2 μs/包 | < 3% | 需解析更多头部 |
| TC 带宽限制 | ~3 μs/包 | < 5% | 需统计字节 |
| LSM 进程监控 | ~5 μs/调用 | < 1% | 只在 exec 时触发 |
flowchart TB
PKT2["网络流量"] --> XDP2["XDP 程序<br/>DDoS 防护"] --> TC2["TC 程序<br/>流量控制"]
TC2 --> APP2["应用层"] --> LSM2["LSM BPF<br/>进程监控"]
style XDP2 fill:#bbdefb,stroke:#1565c0
style TC2 fill:#c8e6c9,stroke:#2e7d32
style LSM2 fill:#fff9c4,stroke:#f9a825
flowchart LR
DEPLOY2["部署安全工具"] --> CONFIG["配置策略<br/>TracingPolicy"] --> AUDIT2["审计模式<br/>仅记录"]
AUDIT2 --> ENFORCE["执行模式<br/>阻断"] --> MONITOR["持续监控<br/>Hubble UI"]
style AUDIT2 fill:#fff9c4,stroke:#f9a825
style ENFORCE fill:#ffcdd2,stroke:#c62828
graph TB
subgraph 安全工具架构
XDP3["XDP 防火墙<br/>L3/L4 过滤"]
TC3["TC 流量控制<br/>限速/分流"]
LSM3["LSM 进程监控<br/>行为检测"]
end
XDP3 --> TC3 --> LSM3
style XDP3 fill:#bbdefb,stroke:#1565c0
style TC3 fill:#c8e6c9,stroke:#2e7d32
style LSM3 fill:#fff9c4,stroke:#f9a825
七、总结
上一章深入解读了eBPF 生产部署实践的内部机制。
| 层级 | Hook | 功能 | Map 类型 | 详见 |
|---|---|---|---|---|
| XDP | 网卡入口 | IP黑名单、速率限制、SYN Flood | Hash, LRU Hash, Per-CPU Array | Ch7 |
| TC | qdisc | 端口过滤、带宽限制 | Hash | Ch8 |
| LSM | 安全检查点 | 进程执行监控、文件访问控制 | Hash, Ring Buffer | Ch10 |
| 控制平面 | 用户态 | 规则管理、指标导出、事件读取 | cilium/ebpf Go 库 | Ch13 |
Tip
本实战项目综合运用了 eBPF 系列的核心知识:XDP(Ch7)提供最快的数据包拦截,TC(Ch8)提供更灵活的流量控制,LSM(Ch10)提供进程级安全监控,CO-RE(Ch6)保证可移植性,Go eBPF(Ch13)提供用户态管理。三层协同,从网络到系统,构建完整的安全防线。
本系列到此完结。从 eBPF 全景 到本实战章,希望你已经从「听说过 eBPF」进阶到「能用 eBPF 解决真实问题」。
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
综合实战:构建 eBPF 网络安全工具
https://blog.souloss.com/posts/ebpf/ebpf-hands-on-practice/ 部分信息可能已经过时
相关文章 智能推荐
1
eBPF 安全:LSM 与进程监控
eBPF eBPF 不仅是一种可观测性工具,更是一种安全执行机制——LSM BPF 允许在内核安全检查点挂载 eBPF 程序,实现细粒度的安全策略;Tetragon 基于 eBPF 实现实时进程监控和运行时安全。本章详解 LSM BPF 的架构、策略编写、Tetragon 的部署与使用,以及 eBPF 安全的最佳实践。
2
eBPF 验证器:如何保证安全
eBPF eBPF 验证器是 eBPF 安全的基石——它在程序加载时进行静态分析,确保 eBPF 程序不会崩溃内核、不会越界访问内存、不会无限循环。从零讲透验证器的 DAG 验证算法、路径探索机制、安全检查规则,并解析常见的验证失败原因与修复方法。
3
系列导读
eBPF 本系列从 eBPF 的底层原理出发,系统讲解 eBPF 虚拟机、验证器、Map 数据结构、Hook 机制、CO-RE 可移植性,再到 XDP/TC 网络处理、LSM 安全、Cilium 实践、Wasm 融合、Kubernetes 集成与生产部署,带你从「听说过 eBPF」进阶到「能用 eBPF 解决真实问题」。
4
eBPF 网络全景
eBPF eBPF 正在重新定义 Linux 网络栈——从连接跟踪、NAT 到 kube-proxy 替代,从 Socket Filter 到 Sk_msg 重定向,eBPF 提供了比 iptables/netfilter 更高性能、更灵活的网络方案。本章从宏观视角展示 eBPF 网络的全景,详解连接跟踪、NAT、kube-proxy 替代、Socket 层 eBPF,并对比 eBPF 与传统网络方案的架构差异。
5
eBPF 可观测性
eBPF eBPF 最大的应用场景是可观测性——零侵入、低开销、内核级的全链路追踪。本章详解三大可观测性工具链——bpftrace(一行命令追踪内核)、BCC(Python 前端 + 丰富工具集)、Beyla(零侵入应用性能监控),并通过实战展示性能分析、分布式追踪、应用性能监控的完整工作流。






