简化到通配符目标的出口路由

Istio 现在支持带有 DYNAMIC_DNS 解析的通配符 ServiceEntry,允许 Sidecar 将流量直接路由到通配符 HTTPS 目标,同时简化出口配置。

Mar 20, 2026 | 作者 Rudrakh Panigrahi - Salesforce; Translated by Wilson Wu - DaoCloud

概述

控制出站流量是服务网格部署中的常见需求。许多组织通过设置以下参数来配置其服务网格,以仅允许显式注册的外部服务:

meshConfig.outboundTrafficPolicy.mode = REGISTRY_ONLY

通过这种配置,任何外部目标都必须使用诸如 ServiceEntry 之类的资源, 通过完全限定域名和 DNS 解析类型在网格中注册。

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-wikipedia-https
  namespace: istio-system
spec:
  hosts:
  - "www.wikipedia.org"
  ports:
  - name: tls
    number: 443
    protocol: TLS
  location: MESH_EXTERNAL
  resolution: DNS
  exportTo:
  - "*"

然而,某些外部服务会公开许多动态子域,应用程序可能需要访问诸如以下端点:

https://en.wikipedia.org
https://de.wikipedia.org
https://upload.wikipedia.org

随着主机名列表的增长,逐个注册主机名很快就会变得难以管理和扩展。 为了解决这个问题,Istio 需要支持通配符主机名注册。

为什么通配符 HTTPS 出口难以实现

当工作负载发起 HTTPS 连接时,目标主机名通过 TLS 握手中的**服务器名称指示(SNI)**字段进行传输。

例如,调用 https://en.wikipedia.org 的客户端会在 TLS 握手期间, 在 ClientHello SNI 字段中发送主机名 en.wikipedia.org。Istio Sidecar 会拦截出站连接, 并确定目标地址是否已注册以及应如何路由。

然而,Istio 的路由模型通常要求预先知道上游目标地址。即使在路由规则中使用通配符匹配, 最终的上游集群仍然必须对应于静态配置的服务。由于不同的子域名可能解析到不同的端点, 因此直接路由到通配符主机在历史上并非易事。

通过出口网关进行 SNI 路由

这个问题之前在 Istio 博客文章将出口流量路由到通配符目标中讨论过。 该架构包含一个专用的出口网关设置,用作 SNI 转发代理。

使用任意域名进行出口 SNI 路由
应用程序 → Sidecar → 出口网关 → SNI 检测 → 外部目标

上图最初发表于将出口流量路由到通配符目的地

如上图所示:

  1. 应用程序发起 HTTPS 连接。
  2. Sidecar 代理拦截此连接,并向出口网关发起内部 mTLS 连接。
  3. 网关终止此内部 mTLS 连接。
  4. 内部监听器检查原始 TLS 握手中的 SNI 值。
  5. 流量动态转发到从 SNI 中提取的主机名。

实现此功能需要多个自定义资源:

虽然这种方法可行,但它会引入额外的网络跃点,并为该跃点增加一层内部 mTLS。 此外,由于需要大量的自定义配置,操作也变得更加复杂,这些配置难以管理且容易出错。 但最近的改进使得用更简单的配置即可实现相同的结果。

具有 DYNAMIC_DNS 解析的通配符 ServiceEntry

Istio 现在支持在 ServiceEntry 中使用 DYNAMIC_DNS 解析的通配符主机名, 使 Sidecar 代理能够直接路由通配符出站 TLS 流量,而无需出口网关。

例如,以下配置允许访问所有 *.wikipedia.org 端点:

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-wildcard-https
  namespace: istio-system
spec:
  hosts:
  - "*.wikipedia.org"
  ports:
  - name: tls
    number: 443
    protocol: TLS
  location: MESH_EXTERNAL
  resolution: DYNAMIC_DNS
  exportTo:
  - "*"

应用此资源后,网格中的工作负载可以通过此 ServiceEntry 连接到任何匹配的子域。

$ kubectl exec $POD_NAME -n default -c ratings -- curl -sS -o /dev/null -w "HTTP %{http_code}\n" https://de.wikipedia.org && echo "Checking stats after request..." && kubectl exec $POD_NAME -c istio-proxy -- curl -s localhost:15000/clusters | grep "outbound|443||\*\.wikipedia\.org" | grep -E "rq|cx"

HTTP 200
Checking stats after request...
outbound|443||*.wikipedia.org::142.251.223.228:443::cx_active::0
outbound|443||*.wikipedia.org::142.251.223.228:443::cx_connect_fail::0
outbound|443||*.wikipedia.org::142.251.223.228:443::cx_total::3
outbound|443||*.wikipedia.org::142.251.223.228:443::rq_active::0
outbound|443||*.wikipedia.org::142.251.223.228:443::rq_error::0
outbound|443||*.wikipedia.org::142.251.223.228:443::rq_success::0
outbound|443||*.wikipedia.org::142.251.223.228:443::rq_timeout::0
outbound|443||*.wikipedia.org::142.251.223.228:443::rq_total::3

配置工作原理

具有 DYNAMIC_DNS 解析的通配符 ServiceEntry
应用程序 → Sidecar → 外部目的地

使用 resolution: DYNAMIC_DNS 的通配符 ServiceEntry 会导致 Istio 创建一个动态转发代理 (DFP) 集群,该集群会根据 SNI 字段中的主机名转发 TLS 连接。 通配符主机(例如 *.wikipedia.org)首先在网格服务注册表中注册, 以便 Sidecar 能够路由主机名与该模式匹配的出站请求。当工作负载发起 TLS 连接时, 监听器中的 SNI Inspector 会配置为从握手中读取 SNI 值。 然后,DFP 集群会使用该值作为上游主机名来转发连接。 这实际上实现了通配符 HTTPS 出站流量,允许代理动态解析连接并将连接转发到匹配的子域名, 而无需静态配置端点。同时,它还能保持客户端发起的 TLS 会话,原封不动地转发加密流量。

其他用例

这种方法适用于应用程序需要连接到通配符域,同时还要获得网格可观测性和弹性功能的用例。

Ambient 模式下的出口流量

Ambient 网格中,节点级 ztunnel 处理 L4 流量, 可选的 waypoint 代理可在显式连接时应用 L7 策略和遥测数据。 例如,为了处理通过路点的出站流量,以便为调用多个 AWS 服务终端节点保持一致的策略路径, 可以将 ServiceEntry 标记为 istio.io/use-waypoint, 以便控制平面将匹配的流量定向到指定的路点 Gateway

以下示例将 *.amazonaws.com 注册为外部 TLS (443) ServiceEntry, 并将其绑定到名为 waypoint 的 waypoint 网关:

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: amazonaws-wildcard
  namespace: istio-system
  labels:
    istio.io/use-waypoint: waypoint # attached to a waypoint gateway
spec:
  exportTo:
  - .
  hosts:
  - '*.amazonaws.com'
  location: MESH_EXTERNAL
  ports:
  - name: tls
    number: 443
    protocol: TLS
  resolution: DYNAMIC_DNS

发往未知内部目标的流量

调用方配置中可能只包含有限数量的服务,但仍然需要通过 mTLS 连接到其他内部服务。具体设置如下:

apiVersion: networking.istio.io/v1
kind: Sidecar
metadata:
  name: restrict-default
  namespace: default
spec:
  workloadSelector:
    labels:
      app: ratings
  egress:
  - hosts:
    - "istio-system/*"
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: internal-wildcard-http
  namespace: istio-system
spec:
  hosts:
  - "*.svc.cluster.local"
  ports:
  - name: http
    number: 9080
    protocol: HTTP
  location: MESH_INTERNAL
  resolution: DYNAMIC_DNS
  exportTo:
  - "*"
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: internal-wildcard-dr
  namespace: istio-system
spec:
  host: "*.svc.cluster.local"
  trafficPolicy:
    tls:
      mode: MUTUAL_TLS # needs DNS SAN in cert
  exportTo:
  - "*"

即使评分服务在其配置中没有包含其他服务, 它现在也可以通过使用 DNS 动态解析主机名来调用网格中的其他服务:

$ kubectl exec $POD_NAME -n default -c ratings -- curl -sS -o /dev/null -w "HTTP %{http_code}\n" details.default.svc.cluster.local:9080/details/0 && echo "Checking stats after request..." && kubectl exec $POD_NAME -c istio-proxy -- curl -s localhost:15000/clusters | grep "outbound|9080||\*\.svc\.cluster\.local" | grep -E "rq_total|rq_success"

Making test request...
HTTP 200
Checking stats after request...
outbound|9080||*.svc.cluster.local::10.96.35.238:9080::rq_success::1
outbound|9080||*.svc.cluster.local::10.96.35.238:9080::rq_total::1

注意:在此用例中,mTLS 需要证书具有 DNS SAN, 因为 Envoy 的动态转发代理利用主机名执行自动 SAN 验证。

结论

通过引入通配符 ServiceEntry 支持和 DYNAMIC_DNS 解析, Istio Sidecar 代理现在可以直接处理发往通配符域名的 HTTP 和 TLS 出站流量。 这简化了配置,提供了更直接的请求路径,无需中间出站网关即可降低延迟,同时仍能保留现有的安全性和策略控制。

参考

Share this post