Learn from https://github.com/dorkamotorka/transparent-proxy-ebpf
一个使用 eBPF 技术实现的透明代理服务器,可以在内核层面拦截和重定向 TCP 连接,实现无感知的流量代理。
- 内核级别拦截:使用 eBPF 在内核层面拦截 TCP 连接
- 透明代理:客户端无需配置,自动重定向到代理服务器
- 原始目标恢复:通过
SO_ORIGINAL_DST获取客户端真实要连接的目标 - 高性能:避免用户空间和内核空间的频繁切换
- 零配置:客户端应用无需修改即可使用
- cgroup/connect4:拦截客户端的
connect()系统调用,重定向到代理服务器 - sockops:在连接建立后记录端口映射信息
- cgroup/getsockopt:处理代理服务器的
SO_ORIGINAL_DST查询,返回原始目标
客户端应用 → connect(目标服务器) → eBPF拦截 → 重定向到代理服务器
↓
代理服务器 → getsockopt(SO_ORIGINAL_DST) → 获取原始目标 → 连接真实目标
↓
建立双向数据转发通道
├── main.go # 主程序入口
├── bpf/
│ └── proxy.bpf.c # eBPF 程序源码
├── testserver/
│ └── main.go # 测试服务器
├── go.mod # Go 模块定义
└── README.md # 项目文档
- Linux 内核版本 ≥ 4.15(支持 eBPF cgroup 程序)
- Go 1.19+
- clang/LLVM(用于编译 eBPF 程序)
- libbpf 开发库(eBPF 程序运行时库)
- 管理员权限(加载 eBPF 程序需要)
sudo dnf install -y clang llvm kernel-devel kernel-headers libbpf libbpf-devel glibc-develPS:暂时只在自己的电脑 Fedora42 上运行过
git clone https://github.com/kerolt/ktproxy.git
cd ktproxygo generate # 编译 eBPF 程序
go build -o ktproxy main.go# 终端1:启动测试服务器(监听 8080 端口)
go run server/server.go# 终端2:启动透明代理(需要管理员权限)
sudo ./ktproxy# 终端3:测试连接
curl http://localhost:8080当客户端应用调用 connect() 系统调用时,eBPF 程序 handle_cg_connect 会:
- 记录原始目标地址和端口
- 将连接重定向到代理服务器
- 存储 socket cookie 和目标信息的映射
连接建立后,eBPF 程序 handle_sockops 会:
- 记录客户端源端口和 socket cookie 的映射
- 为后续的目标查询做准备
代理服务器调用 getsockopt(SO_ORIGINAL_DST) 时,eBPF 程序 handle_getsockopt 会:
- 通过客户端端口查找 socket cookie
- 通过 socket cookie 查找原始目标信息
- 返回原始目标地址和端口
代理服务器建立到真实目标的连接后,进行双向数据转发。
sudo cat /sys/kernel/debug/tracing/trace_pipe程序运行时会输出详细的连接信息:
=== New Connection ===
Proxy Server: 127.0.0.1:8999
Client: 127.0.0.1:45678
Connecting to target 127.0.0.1:8080
Proxy connection established, from 127.0.0.1:45678 to 127.0.0.1:8080
ss -tulpn | grep :8999sudo bpftool prog list
sudo bpftool map list-
Permission denied
- 确保以管理员权限运行
- 检查 eBPF 内核支持:
grep CONFIG_BPF /boot/config-$(uname -r)
-
Program load failed
- 检查内核版本是否支持 cgroup eBPF
- 确保安装了必要的内核头文件
-
Connection refused
- 检查代理服务器是否正确启动
- 确认端口没有被占用