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

Istio 进阶 — 为什么会那样流

明文被拒绝,是因为只剩下一条过滤器链

在 TT Lab 中继续学习

一句话总结

PeerAuthentication 改变的是接收方 sidecar 的入站监听器。STRICT 会变成只有一条过滤器链,仅接收被判定为 TLS 的连接(必须提供客户端证书,用网格 CA 验证);PERMISSIVE 则是在此基础上再附加一条没有匹配条件的明文链。证书 SAN 中的 SPIFFE ID 就是身份,去掉开头的 spiffe:// 之后得到的字符串,就是 AuthorizationPolicy 中的 principal。

为什么需要它

Kubernetes 网络默认是明文的,Pod 的 IP 也不是身份。Pod 重新启动后 IP 会变,同一个 IP 还可能被别的工作负载继承。所以如果用 IP 来写“只有 orders 才能调用 reviews”,很快就会出错。Istio 把身份绑定在服务账号上。istiod 确认 Pod 的服务账号令牌之后,会签发一份在 SAN 中放入 spiffe://cluster.local/ns/default/sa/orders 之类 URI 的证书,sidecar 之间就用这份证书相互确认对方的身份,通过 mTLS 通信。

问题在于,网格不是一次建成的。sidecar 要按命名空间、按部署依次加入。如果这期间接收方拒绝明文,那么还没有 sidecar 的 Pod 发出的调用就会全部中断。所以默认值是 PERMISSIVE——mTLS 和明文都接收。只要对方有 sidecar,发起调用的 sidecar 就会自动用 mTLS 发送(auto mTLS),所以等注入完成之后,再收紧为 STRICT 即可。PeerAuthentication 就是用来写下这种收紧的资源。

工作原理

istiod 会把 PeerAuthentication 转换为接收方 sidecar 的 virtualInbound(15006)监听器。关键是监听器过滤器 tls_inspector。它查看连接的第一个字节,如果是 TLS ClientHello,就把 transport_protocol 标记为 tls,Envoy 再根据这个标记选择过滤器链。

PeerAuthentication Envoy 的 inbound 监听器
STRICT 一条 transport_protocol: tls 链。require_client_certificate: true,trusted_ca 是网格根证书
PERMISSIVE(默认) 上面这条链,加一条没有匹配条件的明文链
DISABLE 只有明文链
portLevelMtls 另外生成一条以 destination_port 匹配该端口的链

在 STRICT 下,明文连接没有可匹配的链,Envoy 会连 HTTP 响应都不给,直接关闭。客户端看到的是 connection reset,而 Envoy 中只会留下 no_filter_chain_match 统计。portLevelMtls 的编号不是 Service 的 port,而是容器所监听的工作负载端口。因为 iptables 把连接转向 15006 时会保留原始目标端口,所以链看到的就是这个编号。portLevelMtls 只有在带有 selector 的策略中才会被接受。

身份确认分为两层。签名验证只看“是不是网格 CA 签名的”,由同一个 CA 签发的证书,无论是谁的都能通过。“是谁”由 SAN 回答。Istio 主要把 SAN 比对设在发起调用的一侧——客户端把服务器证书的 SPIFFE ID 与服务所期望的账号进行比对,这就是 secure naming。在接收方,“谁可以调用”由 AuthorizationPolicy 负责。mTLS 结束后,Envoy 会把对方证书的 URI SAN 记作该连接的 principal,source.principals: ["cluster.local/ns/default/sa/orders"] 就是与它比对的。写法上只是去掉了 scheme,值是相同的。

在现场相遇的样子

改为 STRICT 之后,只有部分调用出现 connection reset by peer。这是发起调用一侧没有 sidecar 的 Pod(CronJob、去掉了 sidecar 的批处理、网格之外的监控)。接收方的日志里根本没有这个请求——因为在选择链的阶段就被断开了。如果 no_filter_chain_match 在增加,就是这种情况。

想只把健康检查或指标端口保留为明文。用 portLevelMtls 只对那个端口设置 DISABLE。这时如果写成 Service 端口号,就匹配不到任何链,会被悄悄忽略。要写 targetPort。

设置了 AuthorizationPolicy 之后全部返回 403。原因是 principals 前加了 spiffe://,或者信任域不同(不是 cluster.local 的安装),或者调用以明文进入,principal 为空。这三种情况 istioctl validate 都会通过。

官方文档:PeerAuthentication · Security concepts · AuthorizationPolicy · Envoy TLS

下一项实验要做什么

编写 STRICT PeerAuthentication 并进行筛查,然后用 openssl 生成网格 CA 和三份 SPIFFE 证书。再用这些证书,亲手把 STRICT、SAN 匹配器、PERMISSIVE 和端口级例外建成 Envoy 链,确认明文和 mTLS 各自的结果,并从 SAN 中推导出 AuthorizationPolicy 的 principal。