mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
657 字
2 分钟
综合实战:构建 eBPF 网络安全工具
2026-04-19

前 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 点详见
XDPIP 黑名单、速率限制、SYN Flood 防护XDP 入口Ch7 XDP
TC带宽限制、连接跟踪、端口过滤TC ingress/egressCh8 TC
LSM进程执行监控、文件访问控制、提权检测LSM BPFCh10 eBPF安全

二、Layer 1:XDP 防 DDoS#

2.1 XDP 程序#

xdp_security.bpf.c
#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
# 添加黑名单 IP
sudo 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 程序#

tc_security.bpf.c
#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
# 创建 qdisc
sudo tc qdisc add dev eth0 clsact
# 挂载 ingress
sudo 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 程序#

lsm_security.bpf.c
#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 发送包 → 应被 DROP
ping -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 denied

6.2 部署#

# Kubernetes DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ebpf-security
spec:
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/tracing

6.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 FloodHash, LRU Hash, Per-CPU ArrayCh7
TCqdisc端口过滤、带宽限制HashCh8
LSM安全检查点进程执行监控、文件访问控制Hash, Ring BufferCh10
控制平面用户态规则管理、指标导出、事件读取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/
作者
Souloss
发布于
2026-04-19
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

相关文章 智能推荐
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(零侵入应用性能监控),并通过实战展示性能分析、分布式追踪、应用性能监控的完整工作流。