bigywater.tech
微服务 · 83

Sidecar 的流量劫持:iptables 规则做了什么

Sidecar 是怎么把应用的流量接过去的?iptables 的 REDIRECT 规则做了什么?sidecar 怎么知道应用原本要连哪里?为什么必须放行 sidecar 自己的流量?排除端口怎么做?入站流量怎么劫持?这篇在 Linux 容器里用真实的 iptables 和一个 50 行的 sidecar 逐项演示。

2018 ms → 3 ms
应用请求不存在的服务地址 10.99.0.5:8080:没有规则时的超时,与装上 REDIRECT 规则并启动 sidecar 之后的耗时
31
去掉对 sidecar 自己的放行规则后,sidecar 在上限 30 的限制下接受的连接数——它的转发连接又被重定向回自己
0 vs 1
出站规则里排除 3306 端口与去掉排除规则后,sidecar 接受的 3306 连接数

出站劫持

实测 · 本机执行输出
  (a)没有规则:{'ok': False, 'err': 'URLError', 'ms': 2018}
  (b)装上的 nat 表规则(iptables -t nat -S):
      -P PREROUTING ACCEPT
      -P INPUT ACCEPT
      -P OUTPUT ACCEPT
      -P POSTROUTING ACCEPT
      -N MESH_OUT
      -A OUTPUT -p tcp -j MESH_OUT
      -A MESH_OUT -m owner --uid-owner 1337 -j RETURN
      -A MESH_OUT -d 127.0.0.1/32 -j RETURN
      -A MESH_OUT -p tcp -j REDIRECT --to-ports 15001
  应用请求同一个地址:{'ok': True, 'body': 'hello from backend 9000', 'ms': 3}
  sidecar 的日志:
      accept #1 from 172.17.0.2:33650 orig_dst=10.99.0.5:8080 -> 127.0.0.1:9000
  MESH_OUT 链的计数器(pkts bytes target … 一行一条规则,iptables -v -n -x):
          pkts      bytes target     prot opt in     out     source               destination         
             1       60 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner UID match 1337
             0        0 RETURN     all  --  *      *       0.0.0.0/0            127.0.0.1           
             1       60 REDIRECT   tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            redir ports 15001
  OUTPUT 链上所有 TCP 先跳进 MESH_OUT;以 uid 1337(sidecar 自己)发出的和目的地是 127.0.0.1 的先放行(RETURN),其余的 REDIRECT 到 15001;应用连的明明是 10.99.0.5:8080,sidecar 看到的连接来自应用,靠 SO_ORIGINAL_DST 取回应用想连的地址。每条规则的计数都只有 1 个包、60 字节(正好是一个 TCP SYN 包),而不是整个请求的包数:nat 表只处理一条连接的第一个包,同一条连接后面的包按已经建立的转换记录走(这一点是我对 netfilter 的了解,计数器的数字与它一致,没有另外验证)

OUTPUT 链上的 REDIRECT 规则把应用发出的 TCP 连接送到 sidecar,sidecar 用 SO_ORIGINAL_DST 取回原始目的地址。

循环

实测 · 本机执行输出
  应用的结果:{'ok': False, 'err': 'RemoteDisconnected', 'ms': 20}
  sidecar 在 3 s 内接受的连接数:31(上限 30);它们的原始目的地址:['10.99.0.5:8080', '127.0.0.1:9000']
  日志开头:
      accept #1 from 172.17.0.2:33666 orig_dst=10.99.0.5:8080 -> 127.0.0.1:9000
      accept #2 from 127.0.0.1:46012 orig_dst=127.0.0.1:9000 -> 127.0.0.1:9000
      accept #3 from 127.0.0.1:46024 orig_dst=127.0.0.1:9000 -> 127.0.0.1:9000
      accept #4 from 127.0.0.1:46038 orig_dst=127.0.0.1:9000 -> 127.0.0.1:9000
  sidecar 转发时自己发出的连接也被 REDIRECT 回了 15001,变成新的一条连接又交给自己,循环往复;所以出站规则里必须让 sidecar 的流量(按 uid 或 gid 识别)绕过自己

sidecar 自己发出的连接必须按 uid 放行,否则被重定向回自己形成循环。

排除端口

实测 · 本机执行输出
  (a)有排除规则:应用的结果 {'ok': True, 'body': 'hello from backend 3306', 'ms': 1},sidecar 接受的连接数 0
  (b)去掉排除规则:应用的结果 {'ok': True, 'body': 'hello from backend 3306', 'ms': 9},sidecar 接受的连接数 1;日志 ['accept #1 from 172.17.0.2:59876 orig_dst=172.17.0.2:3306 -> 172.17.0.2:3306']
  被排除的端口的连接不经过 sidecar(sidecar 的计数不变);数据库、监控等不想经过代理的流量用这个办法绕开

在 REDIRECT 之前对指定端口 RETURN,这部分连接就不经过 sidecar。

入站劫持

实测 · 本机执行输出
  访问 172.17.0.2:8080 的结果:'hello from backend 8080';访问 172.17.0.2:3306 的结果:'hello from backend 3306'
  入站 sidecar 的日志:
      accept #1 from 172.17.0.3:49512 orig_dst=172.17.0.2:8080 -> 127.0.0.1:8080
  MESH_IN 链的计数器:
             1       60 REDIRECT   tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 redir ports 15006
  8080 的请求先到入站 sidecar,sidecar 用 SO_ORIGINAL_DST 知道它本来要去哪个地址,再转发给应用(这里转发到 127.0.0.1:8080);3306 没有入站规则,直接到应用
ERRORS {}

PREROUTING 链把进入的连接重定向到入站 sidecar,再转发给应用。

分步演示

sidecar 的 iptables 劫持
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。macOS 没有 iptables,所以用 Docker Desktop 的 Linux 容器(python:3.12-alpine,apk 装 iptables v1.8.10 nf_tables,加 NET_ADMIN);规则是 Istio 风格的简化版(没有 IPv6、没有 UDP、没有 DNS 捕获、没有 TPROXY);sidecar 是我写的 Python 脚本,不是 Envoy,只做了字节转发;入站实验用第二个容器去连第一个容器的 IP;每个情形各跑一次
    • 没有验证:Istio 的 istio-iptables 完整规则集(包括 ISTIO_INBOUND、ISTIO_IN_REDIRECT 与 127.0.0.6 的细节);TPROXY 模式;eBPF / ambient 模式替代 iptables;iptables 规则数量很多时的性能;nat 表只处理连接第一个包是我对 netfilter 的了解,计数器的数字与它一致但没有另外验证;Kubernetes init 容器或 CNI 插件怎样装这些规则
    京ICP备2026067502号-1京公网安备11010502063487号