从 PERMISSIVE 一路推到 STRICT
目标
亲手操作 mTLS 策略的三种作用范围及其优先级,并留下一个在不中断运行中服务网格的前提下升级到 STRICT 的实施计划。
为什么重要
从技术上说,切换到 STRICT 只是修改一个字段,但在生产运维中,它却是最容易引发事故的操作之一。原因很简单——原本通过明文进入的路径会毫无预警地全部中断。没有 Sidecar 的定时任务、服务网格外的批处理任务以及自定义探针,平时很难在仪表板上暴露出来,却会在启用 STRICT 的瞬间表现为故障。因此,标准做法是先保持 PERMISSIVE,全面调查明文流量的来源,再从较小的范围开始逐步升级。
另一个必须掌握的概念是方向的区分。PeerAuthentication 管理接收端(入站),DestinationRule 的 trafficPolicy.tls 管理发送端(出站)。两者不一致时,连接会失败,但应用日志中通常只会显示为 503。此外,istioctl analyze 无法发现这种组合问题——旧版本曾有专用分析器,但当前版本的分析器列表中已不存在。因此,将这两个资源成对摆放并由人工直接核对的习惯,本身就是安全保障。
此环境不会发生真实握手,因此评分会检查策略名称、作用范围、字段和静态分析结果。
步骤
**开始前的准备。**每次实验都会启动新的实验 Pod,因此不会保留前一实验的服务网格设置。如果 kubectl get crd peerauthentications.security.istio.io 没有输出,请执行 istioctl manifest generate --set profile=minimal > /root/istio/manifest.yaml,然后将 kubectl apply -f /root/istio/manifest.yaml 执行两次。该清单也会创建 istio-system 命名空间;第 2 步需要在其中放置服务网格全局策略,因此它必须存在。接着执行 kubectl create ns mesh-lab && kubectl label ns mesh-lab istio-injection=enabled,准备工作命名空间。
- 创建
/root/istio/mtls/pa-permissive.yaml——它是仅含一个文档的 PeerAuthentication,metadata.name为default,metadata.namespace为mesh-lab,spec.mtls.mode为PERMISSIVE,并且不要设置selector(这是整个命名空间的策略)。创建后应用它。 - 在
istio-system命名空间中应用名为default的 PeerAuthentication,将spec.mtls.mode: STRICT,并且不设置 selector。然后在/root/istio/mtls/out/scope-note.txt中写明:在服务网格全局、命名空间、工作负载三个层级中,范围越窄、越具体的策略优先。 - 在
mesh-lab中创建 payments 工作负载——ServiceAccountpayments、Servicepayments(端口名称http8080、http-metrics9090)以及 Deploymentpayments(Pod 标签app: payments、serviceAccountName: payments)。然后创建 PeerAuthenticationlegacy-metrics并将它放在mesh-lab中:selector.matchLabels.app为payments,spec.mtls.mode为STRICT,而portLevelMtls中**只能为端口9090**设置mode: DISABLE。 - 在
mesh-lab中创建 DestinationRulepayments-mtls。spec.host为payments,spec.trafficPolicy.tls.mode为ISTIO_MUTUAL。然后在/root/istio/mtls/out/direction-note.txt中写明两者的区别:PeerAuthentication 管理接收端(入站),DestinationRule 管理发送端(客户端/出站)。 - 在
/root/istio/mtls/identity.txt中用准确的一行写出 payments 工作负载的 SPIFFE 身份:spiffe://cluster.local/ns/mesh-lab/sa/payments。然后在/root/istio/mtls/out/identity-note.txt中写明,服务网格中的身份不是 IP 地址,而是服务账号。 - 在
mesh-lab中创建 RequestAuthenticationjwt-payments。selector.matchLabels.app为payments,jwtRules[0].issuer为https://idp.labhub.example/,jwtRules[0].jwksUri为https://idp.labhub.example/.well-known/jwks.json。然后在/root/istio/mtls/out/jwt-note.txt中写明,仅靠该资源无法阻止没有令牌的请求,还需要配合 AuthorizationPolicy。 - 将
mesh-lab中的defaultPeerAuthentication 改为STRICT并应用。在此状态下,应用一个spec.trafficPolicy.tls.mode: DISABLE的 DestinationRule(例如payments-plaintext),并将istioctl analyze -n mesh-lab的结果保存到/root/istio/mtls/out/analyze-conflict.txt。当前版本的分析器列表中没有检查 mTLS 组合冲突的分析器,所以输出中不会显示该冲突——请追加一行,记录哪两项设置彼此不一致。随后删除该 DestinationRule,或将其改为ISTIO_MUTUAL以消除冲突;再次分析并将结果保存到/root/istio/mtls/out/analyze-fixed.txt。mesh-lab中不得遗留任何tls.mode: DISABLE的 DestinationRule。 - 编写不少于 400 字节的
/root/istio/mtls/migration.md。文档中,PERMISSIVE和观测(指标/监控)的内容必须先于(位于更上方的行)STRICT一词出现,并且必须包含按命名空间分阶段应用以及回滚(恢复)方法。最后确保mesh-lab中的defaultPeerAuthentication 处于STRICT状态。
参考
- 每次实验都会启动新的实验 Pod,因此不会保留前一实验的集群状态。所以,将服务网格设置保存为清单,本身就是可复现性。安全策略尤其如此——手动启用的 STRICT 会随集群消失,而存放在代码仓库中的策略可以在下一个集群中原样重现。
- 如果在第 8 步的文档标题中先写
STRICT,顺序检查会失败。标题可写成“mTLS 切换计划”,正文按照阶段 0(观测、保持 PERMISSIVE)→ 阶段 1(消除明文来源)→ 阶段 2(按命名空间启用 STRICT)→ 阶段 3(服务网格全局 STRICT)→ 回滚的顺序编写。 - 在真实生产环境中,可通过
istio_requests_total的connection_security_policy标签查看明文流量比例。此环境没有指标,但在计划中写明这一判断依据才是正确做法。 - 第 3 步的 Deployment 可以复制
/opt/lab/fixtures/istio/inject-target.yaml,再只添加serviceAccountName。 - 常见错误 1:给命名空间全局策略添加 selector。一旦添加 selector,它就会变成工作负载级策略,不再作用于其他工作负载。
- 常见错误 2:将服务网格全局策略放在根命名空间(
istio-system)中,并命名为default,才能成为全局策略。
将命名空间默认策略设为 PERMISSIVE
创建 /root/istio/mtls/pa-permissive.yaml——它是仅含一个文档的 PeerAuthentication,metadata.name 为 default,metadata.namespace 为 mesh-lab,spec.mtls.mode 为 PERMISSIVE,并且不要设置 selector(这是整个命名空间的策略)。创建后应用它。
作用于整个命名空间的策略使用固定名称,并且不设置 selector。切换应从同时接受明文和密文的状态开始。
将服务网格全局默认值升级为 STRICT
在 istio-system 命名空间中应用名为 default 的 PeerAuthentication,将 spec.mtls.mode: STRICT,并且不设置 selector。然后在 /root/istio/mtls/out/scope-note.txt 中写明:在服务网格全局、命名空间、工作负载三个层级中,范围越窄、越具体的策略优先。
服务网格全局策略放在根命名空间中。并请在说明中整理三个作用层级中哪一个优先。
只为单个工作负载的单个端口开放例外
在 mesh-lab 中创建 payments 工作负载——ServiceAccount payments、Service payments(端口名称 http 8080、http-metrics 9090)以及 Deployment payments(Pod 标签 app: payments、serviceAccountName: payments)。然后创建 PeerAuthentication legacy-metrics 并将它放在 mesh-lab 中:selector.matchLabels.app 为 payments,spec.mtls.mode 为 STRICT,而 portLevelMtls 中**只能为端口 9090**设置 mode: DISABLE。
例外应尽可能缩小。用 selector 选择工作负载,默认仍保持 STRICT,只单独指定有问题的端口。
声明发送端的 mTLS
在 mesh-lab 中创建 DestinationRule payments-mtls。spec.host 为 payments,spec.trafficPolicy.tls.mode 为 ISTIO_MUTUAL。然后在 /root/istio/mtls/out/direction-note.txt 中写明两者的区别:PeerAuthentication 管理接收端(入站),DestinationRule 管理发送端(客户端/出站)。
接收端和发送端由不同资源管理。应使用专门的模式来表示采用服务网格签发的证书,而不是自行指定证书。
写出工作负载的身份
在 /root/istio/mtls/identity.txt 中用准确的一行写出 payments 工作负载的 SPIFFE 身份:spiffe://cluster.local/ns/mesh-lab/sa/payments。然后在 /root/istio/mtls/out/identity-note.txt 中写明,服务网格中的身份不是 IP 地址,而是服务账号。
三个组成部分按顺序出现,而且作为该身份依据的对象必须真实存在于集群中。
创建最终用户 JWT 验证策略
在 mesh-lab 中创建 RequestAuthentication jwt-payments。selector.matchLabels.app 为 payments,jwtRules[0].issuer 为 https://idp.labhub.example/,jwtRules[0].jwksUri 为 https://idp.labhub.example/.well-known/jwks.json。然后在 /root/istio/mtls/out/jwt-note.txt 中写明,仅靠该资源无法阻止没有令牌的请求,还需要配合 AuthorizationPolicy。
验证签名需要同时提供签发者和公钥位置。还请在说明中写明,该资源单独使用时无法阻止什么。
制造并解决 STRICT 与 DISABLE 的冲突
将 mesh-lab 中的 default PeerAuthentication 改为 STRICT 并应用。在此状态下,应用一个 spec.trafficPolicy.tls.mode: DISABLE 的 DestinationRule(例如 payments-plaintext),并将 istioctl analyze -n mesh-lab 的结果保存到 /root/istio/mtls/out/analyze-conflict.txt。当前版本的分析器列表中没有检查 mTLS 组合冲突的分析器,所以输出中不会显示该冲突——请追加一行,记录哪两项设置彼此不一致。随后删除该 DestinationRule,或将其改为 ISTIO_MUTUAL 以消除冲突;再次分析并将结果保存到 /root/istio/mtls/out/analyze-fixed.txt。mesh-lab 中不得遗留任何 tls.mode: DISABLE 的 DestinationRule。
接收端要求加密,而发送端坚持明文时,连接会失败。请分别保留修复前后的分析结果,并且不要遗留任何不一致设置。
编写分阶段切换计划并完成切换
编写不少于 400 字节的 /root/istio/mtls/migration.md。文档中,PERMISSIVE 和观测(指标/监控)的内容必须先于(位于更上方的行)STRICT 一词出现,并且必须包含按命名空间分阶段应用以及回滚(恢复)方法。最后确保 mesh-lab 中的 default PeerAuthentication 处于 STRICT 状态。
文档中词语出现的顺序就是阶段顺序。先写观测和 PERMISSIVE,再写 STRICT,同时加入恢复方法。