Envoy首次HTTPS请求拦截正常,后续请求为何绕过路由走透传?
环境与需求
在Kubernetes中运行Envoy,通过iptables将目标主机example.com的所有HTTPS流量重定向至Envoy的15000端口,使用的iptables命令:
iptables -t nat -A OUTPUT -d example.com -p tcp --dport 443 -j REDIRECT --to-port 15000
核心需求:Envoy使用自定义证书终止TLS,拦截example.com的/service/greet路径请求并转发至interceptor集群,其余流量透明转发至原上游主机。
简化版Envoy配置如下:
listeners: - name: https_listener address: socket_address: address: 0.0.0.0 port_value: 15000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_https route_config: name: local_route virtual_hosts: - name: interceptor domains: - "example.com" routes: - match: path: "/service/greet" route: cluster: interceptor - match: prefix: "/" route: cluster: passthrough http_filters: - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: interceptor type: STRICT_DNS load_assignment: cluster_name: interceptor endpoints: - lb_endpoints: - endpoint: address: socket_address: address: my.proxy.com port_value: 8080 - name: passthrough type: ORIGINAL_DST lb_policy: CLUSTER_PROVIDED original_dst_lb_config: {}
问题现象
首次调用/service/greet可成功拦截并路由至interceptor集群,但后续对同一路径的请求有时会绕过Envoy,直接发送至原上游主机example.com。
核心原因分析
该问题确实与连接复用、ORIGINAL_DST集群特性直接相关,具体逻辑链如下:
首次请求流程:
客户端发起HTTPS请求,iptables将TCP连接重定向到Envoy的15000端口;Envoy终止TLS后解析请求的Host与路径,匹配/service/greet路由规则,转发至interceptor集群;此时客户端与Envoy之间的连接会被复用(HTTP/1.1 Keep-Alive或HTTP/2多路复用机制)。后续请求绕过的触发条件:
- 当复用连接上出现非
/service/greet的请求时,Envoy会将其路由到passthrough集群(ORIGINAL_DST类型);该集群特性是直接转发到原始目标地址,Envoy会为该连接创建到example.com:443的后端连接并复用。 - 当后续在客户端复用连接上再次发送
/service/greet请求时,Envoy的HTTP连接管理器会直接复用已有的后端连接(到example.com),而非重新匹配路由规则——因为连接级别的路由决策已在首次透传请求时确定,后续同连接的请求会沿用该后端连接,导致路径匹配失效。
- 当复用连接上出现非
另外,简化配置中缺少TLS终止的证书配置,这会导致Envoy无法正确处理HTTPS流量,可能加剧连接复用后的规则匹配异常(你提到首次请求成功,说明实际配置中应该有证书,但简化版本未体现)。
解决方法
1. 临时应急:禁用连接复用
在HTTP连接管理器中禁用Keep-Alive,强制每个请求使用新连接,避免复用导致的规则绕过:
typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_https common_http_protocol_options: idle_timeout: 0s connection_keepalive: keepalive: false
注:该方法会牺牲性能,仅作为临时应急方案。
2. 推荐方案:配置路由与连接隔离
- 为不同路由配置独立连接池:确保拦截请求与透传请求使用完全独立的后端连接池,避免复用冲突;
- 强制请求级路由匹配:在
virtual_host中添加请求头过滤器,确保每个请求都重新执行路由规则匹配,而非沿用连接的后端决策; - 补全TLS终止配置:在
filter_chains中添加证书配置,确保Envoy能正确终止HTTPS流量:filter_chains: - tls_context: common_tls_context: tls_certificates: - certificate_chain: filename: "/path/to/cert.pem" private_key: filename: "/path/to/key.pem" filters: # 原有的http_connection_manager配置
3. 优化iptables规则
确保iptables规则优先级最高,避免被其他规则覆盖:
iptables -t nat -I OUTPUT -d example.com -p tcp --dport 443 -j REDIRECT --to-port 15000
使用-I将规则插入到OUTPUT链的最前面,确保所有匹配流量都被重定向至Envoy。
内容的提问来源于stack exchange,提问作者Charles d'Avernas

