Envoy 双代理(Double Proxy)部署解析:近用户侧终止 TLS 与基于双向认证、证书钉扎的安全回传链路
本文以 Envoy 官方文档中的 double proxy(双代理)部署类型为主线,讲解其设计动机——把 TLS 与客户端连接的终止点尽量靠近用户,再通过长期存活的 HTTP/2、HTTP/3 连接把流量回传(backhaul)到主数据中心的 front proxy。文中将结合仓库内的两份配置模板 configs/envoy_double_proxy.template.yaml 与 configs/envoy_front_proxy.template.yaml 逐段剖析监听器、回传集群、双向 mTLS 与证书钉扎的关键字段,并给出在仓库中实际生成可运行示例配置的操作步骤,帮助读者完整掌握多地域 Envoy 边缘架构的落地方案。
一、部署背景:为什么需要 Double Proxy
在 部署类型总览 中,Envoy 把推荐部署形态按复杂度递增划分为三类:service to service、front proxy、double proxy(见 deployment_types.rst 的 toctree 顺序)。double proxy 是其中复杂度最高的一种:它在 front proxy 配置 之外,再叠加一层部署在用户就近地域的 Envoy 集群,专门负责接入外部流量并回传给主数据中心。
原文档(double_proxy.rst)给出的核心理由是:尽可能靠近用户终止 TLS 和客户端连接。这样做带来的收益包括:
- TLS 握手往返时间更短(握手 RTT 从"用户—主数据中心"缩短为"用户—就近地域");
- TCP 拥塞窗口(CWND)扩张更快(初始慢启动阶段就在短 RTT 链路上完成);
- 公网长链路上丢包概率更低,重传代价更小。
与之对应的是连接复用策略:在 double proxy 侧终止的客户端连接,会被复用到通向主数据中心的长期存活 HTTP/2 或 HTTP/3 连接上。也就是说,用户侧是大量短生命周期、按请求划分的 TCP/TLS 连接,而骨干链路上只有少量长连接,两侧由 Envoy 的连接池机制解耦。
上图即原文档引用的 double_proxy.svg:region 1 中的 front Envoy(double proxy 角色)终止用户 TLS,通过回传链路把流量送到 region 2 主数据中心的 front Envoy 集群。原文档还特别指出:region 1 的 front Envoy 通过 TLS 双向认证(mutual authentication)加证书钉扎(pinned certificates) 向 region 2 的 front Envoy 证明自己的身份。这一步的意义在于——只有被认证过的对端发来的请求,region 2 的 Envoy 才可以信任其中本来不可信的元素,典型如 x-forwarded-for 请求头:普通外部请求携带的 XFF 可以被任意伪造,而来自已认证 double proxy 的 XFF 则包含了真实的客户端 IP,因此 region 2 侧可以安全地将其用于限流、审计、地理判断等逻辑。
二、Double Proxy 侧配置剖析
原文档末尾提到源码发行版包含示例 double proxy 配置(原文引用的 install_sandboxes_double_proxy 锚点在现仓库中已不可用,当前对应入口是 配置生成器文档)。实际模板位于 configs/envoy_double_proxy.template.yaml,下面按结构逐段拆解。
2.1 监听器:面向用户的 9300/9301 端口
模板通过 Jinja2 宏 listener(protocol, address, port_value, tls, proxy_proto, tracing) 生成两个监听器(envoy_double_proxy.template.yaml#L96-L103):
0.0.0.0:9300:TCP 监听器,开启 TLS,对外承担 443 端口的流量;0.0.0.0:9301:TCP 监听器,不开 TLS,对外承担 80 端口的流量。
两者都假设前置一台支持 Proxy Protocol 的 TCP 负载均衡器(如 ELB),因此都挂了 listener filter:
listener_filters:
- name: envoy.filters.listener.proxy_protocol
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol
由于真实客户端地址由 LB 通过 PROXY protocol 头传递,HCM 中同时设置了 use_remote_address: true(仅在 proxy_proto 为真时渲染),使 Envoy 把 PROXY protocol 里的地址当作流信息中的 remote address,x-forwarded-for 等由 Envoy 基于它正确构建。
TLS 监听器的 DownstreamTlsContext 使用 certs/servercert.pem / certs/serverkey.pem 这对证书,并声明 alpn_protocols: [h2, http/1.1]——即用户侧同时支持 HTTP/2 与 HTTP/1.1(codec_type: AUTO 由 ALPN 协商结果决定使用哪种 codec)。
2.2 HTTP 处理链路:健康检查、缓冲与回传路由
每个监听器的 filter chain 里挂载 envoy.filters.network.http_connection_manager,其内部结构(envoy_double_proxy.template.yaml#L33-L94)要点如下:
- 路由:单一 virtual host
local_service(domains:["*"]),所有/前缀请求都路由到backhaul集群;timeout: 20s并注释说明"一般允许 front proxy 控制超时,这里作为兜底(backstop)"。 - health_check 过滤器:
pass_through_mode: false,对:path精确匹配/healthcheck的请求就地应答,不穿透到回传链路——双代理自身存活探测不依赖骨干链路。 - buffer 过滤器:
max_request_bytes: 5242880(5 MB),在 double proxy 侧缓冲完整请求体后再回传。 - access_log:只记录"错误访问日志"到
/var/log/envoy/access_error.log,过滤条件是or_filter——状态码 ≥ 500、或耗时 ≥ 1000ms、或请求可被追踪(traceable_filter)三者取一,且 500/1000 阈值可用 runtime key(access_log.access_error.status/...duration)在线调整。 - common_http_protocol_options.idle_timeout: 840s:用户侧空闲连接 14 分钟后回收。
2.3 回传集群 backhaul:mTLS + SAN 钉扎 + 显式 HTTP/2
double proxy 的核心配置在 backhaul 集群(envoy_double_proxy.template.yaml#L118-L158):
- name: backhaul
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: backhaul
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: front-proxy.yourcompany.net
port_value: 9400
protocol: TCP
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain:
filename: certs/clientcert.pem
private_key:
filename: certs/clientkey.pem
validation_context:
trusted_ca:
filename: certs/cacert.pem
match_typed_subject_alt_names:
- san_type: DNS
matcher:
exact: "front-proxy.yourcompany.net"
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options: {}
common_http_protocol_options:
max_requests_per_connection: 25000
各字段的含义与作用:
| 字段 | 作用 |
|---|---|
type: STRICT_DNS | 回传目标 front-proxy.yourcompany.net 通过 DNS 解析,解析结果严格用于构造 endpoints |
port_value: 9400 | 指向主数据中心 front proxy 专收双代理回传流量的监听端口(见第三节) |
tls_certificates(clientcert.pem/clientkey.pem) | 回传链路客户端证书,用于向对端完成 mTLS 身份证明 |
trusted_ca(cacert.pem) | 常规 CA 链验证 |
match_typed_subject_alt_names(DNS 精确匹配) | 在 CA 验证之外,额外钉扎对端证书的 SAN 必须精确等于 front-proxy.yourcompany.net,缩小被信任的对端范围 |
explicit_http_config.http2_protocol_options | 显式固定回传连接使用 HTTP/2,即原文档所说"复用到长期存活的 HTTP/2 连接" |
max_requests_per_connection: 25000 | 每连接最多复 2.5 万个请求后关闭重连。模板注释解释了动机:回传连接数量少,连接级负载不均会持续存在,限制单连接请求数可周期性换连接、让流量摊得更均匀 |
值得强调的是,UpstreamTlsContext 里"客户端证书 + CA 验证 + SAN 精确匹配"三者组合,正是原文档所述"TLS 双向认证与钉扎证书"在 double proxy 这一侧的具体形态:double proxy 出示自己的客户端证书给 region 2,同时它只信任 SAN 精确匹配的 region 2 服务器证书。
此外模板还包含运维相关配置:stats_sinks 将指标发送到本机 127.0.0.1:8125 的 statsd;layered_runtime 分 root/override/admin 三层挂载磁盘 runtime 配置(/srv/configset/envoydata/current 下的 envoy、envoy_override 子目录);admin 只监听 127.0.0.1:9901;flags_path: /etc/envoy/flags 用于记录启动参数。
三、Front Proxy 侧:9400 端口回传监听器与证书钉扎
回传链路的另一端在 configs/envoy_front_proxy.template.yaml 中定义。front proxy 共三个监听器(envoy_front_proxy.template.yaml#L103-L111):
# TCP listener for backhaul traffic from the double proxy.
# See envoy_double_proxy.template.json
- {{ listener("TCP", "0.0.0.0", "9400", True, True, tracing, pin_double_proxy_client=True)|indent(2) }}
9400 端口与 double proxy 模板中 backhaul 集群的端口一一对应,开启 TLS 与 Proxy Protocol,并传入 pin_double_proxy_client=True。该开关触发宏内的一段额外校验配置(envoy_front_proxy.template.yaml#L29-L36):
validation_context:
trusted_ca:
filename: certs/cacert.pm
#This should be the hash of the /etc/envoy/envoy-double-proxy.pem cert used in the
#double proxy configuration.
verify_certificate_hash: "000000000000000000000000000000000000000000000000000000000000000000"
模板注释明确说明:verify_certificate_hash 应填写 double proxy 客户端证书的 SHA-256 指纹(即 double proxy 侧 certs/clientcert.pem 的哈希)。这就是"pinning(证书钉扎)"的落点——front proxy 不再泛泛信任整个 CA 下签发的所有客户端证书,而是只接受指纹完全匹配的那一张客户端证书。
把两侧配置对照起来,原文档描述的互信关系就完整闭合了:
- double proxy → front proxy:double proxy 用客户端证书做 mTLS 身份证明;front proxy 用 CA 链 +
verify_certificate_hash精确锁定这张证书。 - front proxy → double proxy:front proxy 的服务器证书由 double proxy 侧
trusted_ca验证,并叠加match_typed_subject_alt_names的 DNS 精确匹配钉扎。
正因 region 2 可以确凿地知道"这条 9400 连接的另一端就是我签发的双代理",它才可以信任回传请求中本不可信的元素——原文档以 x-forwarded-for 为例:double proxy 在 HCM 中 use_remote_address: true 拿到 LB 通过 PROXY protocol 传来的真实客户端 IP,region 2 侧据此得到的 XFF 才有可信度,可用于按真实客户端 IP 做限流、审计与风控。
front proxy 的其余部分则是一个标准 front proxy 部署:9300/9301 对外监听、ratelimit 全局限流过滤器(gRPC 指向 ratelimit 集群)、健康检查过滤器、5 MB buffer、840s idle timeout 与同样的错误访问日志过滤;上游服务集群由 routing_helper.template.yaml 的宏展开,并假设发现服务运行在 discovery.yourcompany.net。
四、生成可运行的示例配置
仓库用 configs/configgen.py 基于 Jinja2 把上述模板渲染成完整 YAML。其中 double proxy 的生成逻辑见 configgen.py#L119-L125:
# Generate a demo config for the double proxy. This sets up both an HTTP and HTTPS listeners,
# and backhauls the traffic to the main front proxy.
generate_config(
SCRIPT_DIR,
'envoy_double_proxy.template.yaml',
'{}/envoy_double_proxy.yaml'.format(OUT_DIR),
tracing=tracing_enabled)
按照 配置生成器文档,在仓库根目录执行:
mkdir -p generated/configs
bazel build //configs:example_configs
tar xvf $PWD/bazel-out/k8-fastbuild/bin/configs/example_configs.tar -C generated/configs
即可得到包含 envoy_double_proxy.yaml、envoy_front_proxy.yaml、envoy_service_to_service.yaml 在内的完整展开配置。使用示例配置时需要自行满足其外部假设:front-proxy.yourcompany.net、discovery.yourcompany.net 等 DNS 记录可用,certs/ 目录下的 CA、服务端与客户端证书成对配置,且两侧钉扎值相互匹配(double proxy 的 SAN 精确匹配值、front proxy 的证书指纹)。
五、要点小结
- 拓扑动机:double proxy 把 TLS/客户端连接终止点下沉到用户就近地域,用短 RTT 换取更快的握手与拥塞窗口扩张,同时把骨干流量收敛为少量长生命周期 HTTP/2(或 HTTP/3)回传连接。
- 配置分工:double proxy 侧(
configs/envoy_double_proxy.template.yaml)负责 9300/9301 对外监听、/healthcheck本地应答、5 MB 请求缓冲、指向front-proxy.yourcompany.net:9400的backhaulSTRICT_DNS 集群,以及"显式 HTTP/2 +max_requests_per_connection: 25000强制轮换连接"的回传连接池策略。 - 安全互信:回传链路是双向钉扎的 mTLS——double proxy 侧用
trusted_ca+match_typed_subject_alt_names(DNS 精确匹配)验证 front proxy 证书;front proxy 侧用trusted_ca+verify_certificate_hash(客户端证书 SHA-256 指纹)锁定 double proxy 身份。 - 信任传递:正因为对端身份被钉扎确认,region 2 的 front proxy 才能信任来自 double proxy 请求中的
x-forwarded-for等原本可被伪造的请求元素,使多地域部署下基于真实客户端 IP 的策略(限流、审计)仍然成立。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



