HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • 代理技术全栈手册

    • 代理技术全栈手册 - HiHuo
    • 原理篇

      • 第01章 代理是什么:正向 / 反向 / 透明 / 隧道的统一模型
      • 第02章 代理与网络层级:L3 / L4 / L5 / L7 在哪里截断流量
      • 第03章 一个请求穿过代理的一生:连接生命周期全景
    • 协议篇

      • 第04章 HTTP 代理协议:绝对 URI、CONNECT 隧道、转发头与连接池
      • 第05章 HTTPS 与 TLS 代理:终止 / 透传 / MITM / SNI / mTLS
      • 第06章 SOCKS 协议:SOCKS4/4a/5 与 UDP ASSOCIATE 报文级解析
      • 第07章 HTTP/2、gRPC 与 HTTP/3(QUIC) 代理的挑战
      • 第08章 代理自动配置:PAC / WPAD / 系统代理 / NO_PROXY
    • 层级与转发篇

      • 第09章 L4 代理:TCP/UDP 转发与连接级负载均衡
      • 第10章 L7 代理:协议感知与基于内容的路由
      • 第11章 透明代理:iptables REDIRECT/DNAT、TPROXY 与 eBPF 劫持
      • 第12章 数据搬运的艺术:splice / sendfile / 零拷贝 / io_uring
    • 组件横评篇

      • 第13章 Nginx / OpenResty:反向代理、upstream 与 Lua 可编程
      • 第14章 HAProxy:L4/L7、ACL、健康检查与 stick table
      • 第15章 Envoy:xDS 动态配置与 filter chain,为何是云原生数据面
      • 第16章 Traefik / Caddy:自动服务发现与自动 HTTPS
      • 第17章 Squid 与正向/缓存代理:企业出网、缓存与审计
      • 第18章 mitmproxy:抓包、改包、脚本化调试
      • 第19章 内网穿透与隧道:frp / gost / SSH 隧道 / ngrok
      • 第20章 科学上网生态的技术原理(技术中立)
    • 多语言手写篇

      • 第21章 Go:100 行手写 HTTP/CONNECT + SOCKS5 代理
      • 第22章 Rust:基于 tokio 的高性能 TCP 代理
      • 第23章 Python:asyncio 实现,适合调试与脚本
      • 第24章 C:epoll 裸写与零拷贝,及语言选型对比
    • 容器与K8s篇

      • 第25章 Docker 里的代理:HTTP_PROXY、build/pull 与 daemon 配置
      • 第26章 Sidecar 与流量劫持:Istio init-container 的 iptables 原理
      • 第27章 Ingress 与南北流量:Ingress-nginx 与 Gateway API
      • 第28章 Egress 与出网治理:出口网关、registry mirror、审计
      • 第29章 Service Mesh 数据面:Envoy Sidecar 全链路
    • 进阶篇

      • 第30章 可编程代理:Lua / Wasm / eBPF / xDS,代理的"软件定义"
      • 第31章 性能调优:并发模型、连接池、超时与重试、压测
      • 第32章 排错决策树:502 / 504 / 握手失败 / 环路 / 泄漏
      • 第33章 代理安全:开放代理、SSRF、凭证泄漏与攻击面
    • 底层机制篇

      • 第34章 代理的背压与流控:一个代理最难的部分
      • 第35章 socket 与 TCP 状态机:半关闭、超时、连接生命周期
      • 第36章 HTTP/2 帧、流控与 HPACK:h2 代理的内部机制
      • 第37章 负载均衡算法推导与韧性状态机
      • 第38章 Capstone:把玩具代理改造成生产级骨架
    • 综合实战篇

      • 第39章 企业多跳转发链:拓扑、协议矩阵与贯穿性难题
      • 第40章 端到端实战:把 6 类流量全代理通
      • 第41章 更刁钻的流量:gRPC、长轮询、WebRTC、大文件、双向流
      • 第42章 可落地完整参考实现:一套能跑的多协议转发栈
    • 附录

      • 附录 A:代理协议报文速查(HTTP / SOCKS / PAC / PROXY protocol)
      • 附录 B:组件选型决策树
      • 附录 C:抓包与命令速查

第02章 代理与网络层级:L3 / L4 / L5 / L7 在哪里截断流量

学习目标

  • 理解"代理工作在第几层"到底意味着什么——它在哪一层重建了语义
  • 分清 L4 代理与 L7 代理的本质差异:看得到什么、看不到什么、能做什么
  • 掌握 SNI 嗅探这种"L4 之上、L7 未满"的中间技巧,及 ECH 对它的冲击
  • 用 socat(L4)和 nginx(L7)亲手验证"层级决定能力"

前置知识

  • 第01章 代理统一模型
  • 网络手册 · 网络模型与数据流转(OSI / TCP-IP 分层)

原理

"工作在第几层"到底在说什么

第01章 说过:代理把一条连接拆成两条。那么问题来了——拆开后,代理在转发前,把字节流解析到了哪一层?

代理"工作在第 N 层" = 它把经过的流量解析、理解、重建到了 OSI 第 N 层。 解析得越高,它懂得越多、能做的越多,但开销也越大。

一个形象的比喻:流量是一封层层套娃的信封(以太帧里装 IP 包,IP 包里装 TCP 段,TCP 段里装 HTTP 报文)。

  • L4 代理只拆到 TCP 这层信封:它知道"这是去 443 端口的一串字节",但不拆 HTTP 那层,看不懂里面写了啥。
  • L7 代理一路拆到 HTTP 报文:它能读 URL、方法、Header,甚至改写 Body。

逐层看代理

        ┌─────────────────────────────────────────────────────────┐
  L7    │ 应用层  HTTP/gRPC/MySQL    ← L7 代理在这里:懂 URL/Header  │  Nginx, Envoy, HAProxy(http)
        ├─────────────────────────────────────────────────────────┤
  L5/6  │ 会话/表示  TLS 会话/SOCKS  ← SOCKS 代理在这里:管会话不解内容│  SOCKS5, stunnel
        ├─────────────────────────────────────────────────────────┤
  L4    │ 传输层  TCP/UDP 端口        ← L4 代理在这里:只见端口+字节流 │  HAProxy(tcp), LVS, socat
        ├─────────────────────────────────────────────────────────┤
  L3    │ 网络层  IP 路由/NAT        ← 严格说是"转发"不是代理        │  iptables DNAT, IPVS
        └─────────────────────────────────────────────────────────┘

L3:IP 层——这其实是"转发/NAT",不算真代理

L3 设备改写的是 IP 头(源/目的 IP),做的是路由和 NAT,并不会拆成两条独立 TCP 连接——它在同一条连接上改地址。所以严格说 NAT 不是代理。但它和代理关系密切:透明代理依赖 L3 的 DNAT/REDIRECT 把流量"拐"到代理端口(见 第11章)。记住这条边界:改地址 ≠ 代理;拆连接 = 代理。

L4:传输层——只见"端口 + 字节流"

L4 代理在两条 TCP(或 UDP)连接间搬运字节。它知道:源/目的 IP、端口、这是一条 TCP 连接。它不知道:里面是 HTTP 还是 SSH 还是 MySQL。

  • 能做:按"目标端口/IP"转发、连接级负载均衡、TCP 层限流、做 TLS 透传(密文原样转发)
  • 不能做:按 URL/Path 路由、改 HTTP Header、缓存、按域名分流(除非用下面的 SNI 嗅探)
  • 优点:快、省、协议无关——一个 L4 代理能代理任意 TCP 协议
  • 代表:HAProxy 的 mode tcp、LVS/IPVS、socat、kube-proxy 本质也是 L4

L5/L5.5:会话层——SOCKS 与隧道的位置

SOCKS 代理(第06章)和 HTTP CONNECT 隧道很特殊:它们建立了一个会话("帮我连到 host:port"),但建立之后对应用数据无感,只做盲转。所以常把它们归在会话层(L5)——比纯 L4 多了"按需建会话、可带认证、可解析目标域名"的能力,但又不像 L7 那样理解应用协议。

这解释了 SOCKS 的杀手锏:协议无关又能远端解析 DNS。它比 L4 多懂一点(目标地址、认证),又比 L7 通用(不挑应用协议)。

L7:应用层——懂得最多,也最重

L7 代理把字节流完整解析成应用层消息对象(一个 HTTP 请求、一个 gRPC 调用),转发前可以任意检视和改写。

  • 能做:按 Host/Path/Header/Method/Cookie 路由、改写头与体、缓存、压缩、鉴权、限流、灰度、可观测(每请求埋点)
  • 代价:要完整解析+缓冲,CPU 和延迟开销最大;且挑协议——一个 HTTP 代理代理不了 MySQL
  • 代表:Nginx、Envoy、HAProxy 的 mode http、Traefik、API 网关

一张表收口:层级即能力

维度L3 转发L4 代理L5 SOCKS/隧道L7 代理
看得到IPIP+端口+字节流目标 host:port、认证完整应用报文
看不到端口以上应用协议应用数据—(全看得到)
按域名/路径路由❌❌(除非 SNI 嗅探)按 host(不按 path)✅
改写 Header/Body❌❌❌✅
协议无关(能转任意 TCP)✅✅✅❌(挑协议)
TLS 处理透传透传 / SNI 嗅探透传终止 / MITM
相对开销最低低低高
典型实现iptables/IPVSHAProxy(tcp)/LVSSOCKS5/CONNECTNginx/Envoy

选层心法:能在低层解决就别上高层。要按 URL 路由、改头、缓存→只能上 L7;只是转发任意 TCP、要极致性能→用 L4;要协议无关又要按域名分流→L4 + SNI 嗅探或 SOCKS。

SNI 嗅探:L4 之上、L7 未满的灰色技巧

有个常见难题:我想做反向代理按域名分流 HTTPS,但又不想解密(不做 TLS 终止)。L4 代理看不到域名(在加密里),L7 又得解密。怎么办?

答案藏在 TLS 握手的第一个包——ClientHello 里的 SNI(Server Name Indication)扩展是明文的!客户端会在这里写明"我要访问 example.com",好让服务器选对证书。于是代理可以只偷看 ClientHello、读出 SNI、据此选后端,然后把整条 TLS 连接当字节流透传——既路由了,又没解密。

Client ──TLS ClientHello (SNI=api.example.com 明文!)──▶ [L4代理 peek SNI]
                                                          │ 读出 api.example.com
                                                          ▼ 据此选后端,之后盲转密文
                                                       [api 后端]
  • 用在哪:HAProxy req.ssl_sni、Nginx ssl_preread、Istio 的 SNI 路由、sing-box/Clash 的分流
  • ⚠️ ECH 的冲击:TLS1.3 的 Encrypted Client Hello(ECH) 会把 SNI 也加密,SNI 嗅探将失效。这是当前代理路由演进的重要变量,详见 第05章。

回答第01章的思考题

CDN 节点对终端用户是反向代理;它"回源"拉源站内容时,这一跳更像正向还是反向?

更像正向——回源时 CDN 是"客户端",主动去访问源站,源站把 CDN 当成一个客户端。这印证了 第01章 的核心:正向/反向是立场,不是绝对身份。同一个节点,对下游是反向、对上游是正向。


️ 实现 / 命令

实验一:L4 代理"不懂"HTTP(socat 一行起)

# 用 socat 起一个纯 L4 代理:监听 8088,盲转到 example.com:80
socat -d -d TCP-LISTEN:8088,fork,reuseaddr TCP:example.com:80 &

# 通过它访问
curl -s -H "Host: example.com" http://127.0.0.1:8088/ -o /dev/null -w "%{http_code}\n"
# 200  —— 能转发

证明它"看不懂":socat 不会、也无法改写任何 HTTP 头或按 path 路由。你给它发什么字节,它原样转什么字节。它甚至不知道这是 HTTP——你把 example.com:80 换成 example.com:22,它照样能转 SSH。这就是 L4 的"协议无关"与"无能为力"的一体两面。

实验二:偷看 TLS ClientHello 里的明文 SNI

# 发起一次 TLS 握手并抓包,看 ClientHello 里的 SNI
sudo tcpdump -i any -n -A 'tcp port 443' &
openssl s_client -connect example.com:443 -servername api.example.com </dev/null 2>/dev/null | head -2

在 tcpdump 输出的第一个 TLS 包里,你能肉眼看到明文的 api.example.com——它没被加密。这就是 L4 代理能做 SNI 路由的物理基础。

# 更直观:用 tshark 直接提取 SNI 字段
sudo tshark -i any -f "tcp port 443" -Y "tls.handshake.extensions_server_name" \
     -T fields -e tls.handshake.extensions_server_name
# api.example.com

实验三:L7 代理能改写、能按 path 路由(nginx)

server {
    listen 8090;
    # 按 path 分流——这是 L4 永远做不到的
    location /api/  { proxy_pass http://backend-api;  proxy_set_header X-Tier api;  }
    location /web/  { proxy_pass http://backend-web;  proxy_set_header X-Tier web;  }
    # 改写响应头——也是 L7 专属
    add_header X-Proxied-By "nginx-L7";
}

/api/ 和 /web/ 去了不同后端,还加了头——因为 Nginx 把字节解析成了 HTTP 对象。把这段和实验一的 socat 对比,"层级即能力"就具象了。


排错 / 选层误区

症状根因修正
用 L7(HTTP)代理去转 MySQL/SSH,连不通L7 代理挑协议,解析不了非 HTTP改用 L4(mode tcp/socat)
想按 URL path 分流,却配了 L4 代理L4 看不到 path改用 L7,或前置 L7
HTTPS 想按域名分流又不想给代理证书该用 SNI 嗅探却做了 TLS 终止用 ssl_preread/req.ssl_sni 透传
上了 ECH/加密 SNI 后 SNI 路由失效ClientHello 被加密,嗅探不到退回 TLS 终止,或基于 IP/端口分流
L4 代理后端拿不到真实客户端 IPL4 不加 X-Forwarded-For用 PROXY protocol(第14章)或上 L7

本章小结

  • 代理"工作在第几层" = 它把流量解析重建到了哪一层;越高越懂、越能改,但越重、越挑协议。
  • L4:见端口+字节流,协议无关,快;L7:见完整报文,能路由/改写/缓存,但挑协议、开销大。
  • SOCKS/隧道在 L5:管会话、可远端解析 DNS,但不碰应用数据,兼具通用性。
  • SNI 嗅探让 L4 能按域名路由 HTTPS 而不解密;ECH 会终结这个技巧。
  • 正向/反向是相对立场(CDN 回源 = 正向),层级才是能力的硬约束。

下一章 第03章,我们顺着一条真实请求,走完"DNS→连代理→认证→连源站→转发→回写→拆连接"的完整生命周期,把前两章的静态模型变成动态影片。

Prev
第01章 代理是什么:正向 / 反向 / 透明 / 隧道的统一模型
Next
第03章 一个请求穿过代理的一生:连接生命周期全景