Envoy透明代理流量劫持部署与调试 - 第3节
3.1 流量劫持基础概念
流量劫持(Traffic Hijacking)是云原生服务网格与透明代理体系中的核心机制。它指在不修改应用代码的前提下,通过操作系统或网络层的拦截手段,将原本发往目标地址的网络数据包重定向到代理进程(如 Envoy)的监听端口,从而实现对流量的拦截、观测、路由与策略执行。
核心目标:让应用无感知地接入代理层,实现零侵入的服务治理。
3.1.1 流量劫持的典型场景
- 入站劫持(Inbound Hijack):将到达 Pod/VM 的入站请求重定向到 Envoy 的入站监听器(通常为 15006 端口)。
- 出站劫持(Outbound Hijack):将应用进程发出的出站连接重定向到 Envoy 的出站监听器(通常为 15001 端口)。
- DNS 劫持:将 DNS 查询重定向到 Envoy 的 DNS 代理,实现基于服务名的路由。
注意:Envoy 本身不执行数据包重定向操作,它依赖操作系统底层机制(如 iptables、eBPF、tproxy)完成劫持,Envoy 只负责接收被重定向过来的流量。
3.2 透明代理与显式代理的区别
在代理架构中,透明代理(Transparent Proxy)与显式代理(Explicit Proxy)是两种截然不同的工作模式。Envoy 在服务网格场景下主要采用透明代理模式。
| 对比维度 | 透明代理 | 显式代理 |
|---|---|---|
| 应用感知 | 应用完全无感知,无需任何配置 | 应用必须显式配置代理地址(如 HTTP_PROXY 环境变量) |
| 劫持方式 | 通过 iptables / eBPF 等内核机制重定向流量 | 应用主动将请求发送给代理服务器 |
| 目标地址可见性 | 代理可以获取原始目标 IP:Port(通过 SO_ORIGINAL_DST 或 TPROXY) | 代理从请求头(如 HTTP Host)或 CONNECT 方法中获取目标 |
| 协议支持 | 支持 TCP/UDP 全协议(四层透明) | 通常只支持 HTTP/HTTPS(七层代理) |
| 配置复杂度 | 需要配置 iptables 规则,对运维要求较高 | 只需在应用中配置代理地址,部署简单 |
| 典型场景 | 服务网格 Sidecar、透明网关 | 企业正向代理、浏览器代理设置 |
重要:Envoy 在 Istio/服务网格中作为 Sidecar 运行时,必须使用透明代理模式。因为微服务应用(如 Java、Go、Python 服务)通常不会主动配置代理,透明劫持是实现零侵入服务治理的唯一可行方式。
3.2.1 透明代理的核心挑战
- 原始目标地址获取:被 iptables REDIRECT 的目标地址会被替换为本机地址,Envoy 需要通过
SO_ORIGINAL_DST套接字选项获取原始目标。 - 环路避免:Envoy 发出的连接必须绕过劫持规则,否则会形成无限递归(Envoy 发出的包又被劫持回自身)。
- UDP 透明代理:UDP 无连接特性使得劫持更加复杂,通常需要 TPROXY 模式支持。
3.3 iptables 基础回顾
iptables 是 Linux 内核的包过滤框架,也是 Envoy 透明代理劫持中最常用的底层工具。理解 iptables 的表(Table)、链(Chain)和规则(Rule)是配置透明代理的前提。
3.3.1 核心概念
| 概念 | 说明 | 透明代理相关 |
|---|---|---|
| 表(Table) | 按功能分类:filter(过滤)、nat(地址转换)、mangle(修改)、raw(原始包) | nat 表用于 REDIRECT/TPROXY;mangle 表用于标记连接 |
| 链(Chain) | 数据包在内核中的检查点:PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING | PREROUTING(入站劫持)、OUTPUT(出站劫持) |
| 规则(Rule) | 匹配条件 + 动作(-j TARGET),按顺序逐条匹配 | -j REDIRECT / -j TPROXY / -j ACCEPT / -j RETURN |
| 匹配条件 | -p 协议、--dport 目标端口、-s 源地址、-d 目标地址、-m owner(进程所有者) | -m owner --uid-owner 匹配 Sidecar 用户,用于排除 Envoy 自身流量 |
3.3.2 透明代理中的关键 iptables 动作
- REDIRECT:将目标端口重定向到本机另一个端口。仅支持 TCP,且会丢失原始目标地址(需通过 SO_ORIGINAL_DST 恢复)。
- TPROXY:透明代理专用动作,支持 TCP 和 UDP,保留原始目标地址,无需特殊套接字选项。需要策略路由配合。
- DNAT:修改目标地址,可用于将流量导向 Envoy 所在节点。
- RETURN:跳过当前链的后续规则,用于排除特定流量(如 Envoy 自身发出的包)。
3.3.3 典型劫持规则示例
# 1. 创建自定义链,便于管理
iptables -t nat -N ISTIO_INBOUND
iptables -t nat -N ISTIO_OUTBOUND
# 2. 入站劫持:将非 localhost 的入站 TCP 流量重定向到 15006
iptables -t nat -A PREROUTING -p tcp -j ISTIO_INBOUND
iptables -t nat -A ISTIO_INBOUND -p tcp --dport 80 -j REDIRECT --to-port 15006
# 3. 出站劫持:将应用发出的 TCP 流量重定向到 15001
iptables -t nat -A OUTPUT -p tcp -j ISTIO_OUTBOUND
# 排除 Envoy 自身流量(假设 Envoy 以 uid 1337 运行)
iptables -t nat -A ISTIO_OUTBOUND -m owner --uid-owner 1337 -j RETURN
# 排除保留地址和已建立的连接
iptables -t nat -A ISTIO_OUTBOUND -d 127.0.0.1/8 -j RETURN
iptables -t nat -A ISTIO_OUTBOUND -m state --state ESTABLISHED -j RETURN
# 重定向剩余出站流量
iptables -t nat -A ISTIO_OUTBOUND -p tcp -j REDIRECT --to-port 15001
实践建议:在生产环境中,建议使用
iptables-save 和 iptables-restore 批量管理规则,避免逐条添加带来的性能开销和原子性问题。Istio 的 istio-iptables.sh 脚本就是基于此原理实现。
3.3.4 环路避免机制
透明代理配置中最容易出现的错误是劫持环路(Hijack Loop):Envoy 发出的数据包又被 iptables 规则重定向回自身。标准解决方案如下:
| 方法 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| UID 排除 | 使用 -m owner --uid-owner 匹配 Envoy 进程的用户 ID,执行 RETURN |
简单可靠,最常用 | 需要确保 Envoy 以独立用户运行 |
| GID 排除 | 使用 -m owner --gid-owner 匹配 Envoy 进程的组 ID |
适合容器场景 | 组 ID 可能被共享 |
| 端口排除 | 排除 Envoy 监听端口(如 15001、15006)的出站流量 | 不依赖进程信息 | 端口可能被其他进程占用 |
| 连接标记 | 在 mangle 表中为 Envoy 发出的连接打标记,在 nat 表中跳过 | 精细控制 | 配置复杂,多表联动 |
避坑指南:如果忘记配置环路避免规则,Envoy 启动后会出现连接超时或 CPU 飙升。排查方法:在 Envoy 节点上执行
iptables -t nat -L -n -v 查看计数器,如果 OUTPUT 链的 REDIRECT 规则计数远大于预期,说明存在环路。