TT Lab
开始
学习 学习路径 课程

Istio 进阶 — 为什么会那样流

在哪里终止 TLS 决定了网关过滤器链的形态

在 TT Lab 中继续学习

一句话总结

Gateway 的 servers[].tls.mode 决定了网关 Envoy 监听器的过滤器链形态。SIMPLE、MUTUAL、OPTIONAL_MUTUAL、ISTIO_MUTUAL 会在链上挂载 DownstreamTlsContext,由网关终止 TLS,再通过 HCM 进行 HTTP 路由。PASSTHROUGH 和 AUTO_PASSTHROUGH 则不解密,只窥视 SNI 来选择链,然后通过 tcp_proxy 把字节原样转交出去。区分两者最可靠的证据是:客户端收到的是谁的证书。

为什么需要它

入口网关是网格之外的客户端最先遇到的 Envoy。在哪里终止 TLS,是安全与运维之间的折中。在网关终止的话,证书可以集中在一处管理,可以查看解密后的 HTTP,按路径、请求头进行路由,还可以设置重试、超时和授权;代价是明文会在网关处暴露一次。需要端到端加密的服务(用自己的证书来验证客户端的支付后端、直接处理 TLS 的数据库),则必须由网关不加干预地转交。

Istio 通过 Gateway 的一个字段来做出这个选择。问题在于,症状彼此相似。“证书名称不匹配”“连接立刻断开”“301 无限循环”,如果不知道 TLS 在哪里终止,都会让人去修改错误的地方。了解各个模式会在 Envoy 中生成什么之后,只需一次 proxy-config listener 就能看出原因。

工作原理

Gateway 只会在网关 Pod 上打开端口并配置证书。流量要去哪里,由指向该 Gateway 的 VirtualService 决定,所以单有一个 Gateway,路由是空的(只有 AUTO_PASSTHROUGH 例外)。各个模式在网关监听器上生成的内容如下。

模式 终止 TLS 的位置 客户端证书 在 Envoy 中生成的内容
SIMPLE 网关 不要求 链上挂载 DownstreamTlsContext(服务器证书)加 HCM
MUTUAL 网关 必须要求 在上面基础上加 require_client_certificate: true 和 validation_context
OPTIONAL_MUTUAL 网关 提供就验证,不提供也能通过 只有 validation_context,关闭强制要求
ISTIO_MUTUAL 网关 istiod 签发的网格证书 通过 SDS 接收工作负载证书和网格根证书并进行验证
PASSTHROUGH 上游 网关不查看 tls_inspector 加 server_names 匹配再加 tcp_proxy
AUTO_PASSTHROUGH 上游 网关不查看 SNI 本身就是 outbound_.<포트>_.<subset>_.<호스트>(占位符依次为端口与主机)形式的集群名称,不需要 VirtualService

证书不是文件,而是通过 SDS 传入的。credentialName: shop-cert 指向与网关位于同一命名空间的 Secret,在 Envoy 配置中则表现为名为 kubernetes://shop-cert 的 SDS 名称。MUTUAL 用于验证的 CA,来自同一个 Secret 的 ca.crt 或 shop-cert-cacert。

路由配置名称也有规则。HTTPS 服务器会为每个 Gateway 单独生成 https.<포트>.<포트이름>.<Gateway이름>.<네임스페이스>(占位符依次为端口、端口名称、Gateway 名称与命名空间)(例如 https.443.https.shop-gw.default),而明文 HTTP 服务器只有一个 http.<포트>(占位符为端口),同一端口上所有 Gateway 的主机都合并在其中。httpsRedirect: true 会在那个明文服务器的 virtual host 中变成一行 require_tls: ALL,使 Envoy 在查看路由之前,就用 301 指向 https。Location 中的主机原样取自请求的 Host 头,所以如果带有端口,端口也会保留。

PASSTHROUGH 中网关之所以能选择目的地,是因为 SNI 以明文形式放在 TLS 的第一条消息(ClientHello)中。tls_inspector 只读取这一项,并与链的 server_names 对照。如果没有匹配的链,连接就在当场关闭,listener.<주소>.no_filter_chain_match(占位符为地址)会增加。Istio 实际生成的 SIMPLE 链上也带有 server_names——这是为了在同一个端口上按主机选择不同的证书。

在现场相遇的样子

明明是 PASSTHROUGH,更换网关证书却毫无作用。客户端从一开始看到的就是后端证书。用 openssl s_client -servername 查看收到的证书的签发者,马上就能看出来。

只有用 IP 连接或较老的客户端会断开连接。如果不发送 SNI,PASSTHROUGH 链就无从选择。curl 只会显示握手失败(rc=35),所以要同时查看网关的 no_filter_chain_match。

改为 MUTUAL 之后,部分客户端说“连接被重置”。在 TLS 1.3 中,拒绝是在客户端以为握手已经完成之后才到达的,所以错误的样子五花八门。如果 Envoy 一侧的 ssl.fail_verify_no_cert 在增加,就是没有提供证书。

重定向指向了奇怪的端口。如果负载均衡器把端口附加在 Host 头上传来,Location 中也会保留。此外,如果负载均衡器的健康检查进入了开启 httpsRedirect 的明文服务器,返回的是 301,可能会被判定为异常。

官方文档:Gateway · Secure Gateways · Envoy TLS

下一项实验要做什么

编写 Gateway,看到 istioctl 要求 SIMPLE 必须提供证书之后,把五种模式转写成 Envoy 的形态。用 openssl 生成 CA、网关、客户端和后端证书,依次启动 SIMPLE、MUTUAL、PASSTHROUGH 监听器,并通过客户端收到的证书指纹,证明 TLS 在哪里终止。最后确认 SNI 不匹配的情形和 httpsRedirect 的 301。