只要立了网关就算拦住了吗
一句话总结
网格中出站流量的默认值是去哪儿都放行(ALLOW_ANY)。收紧它的手段有四个——缩小代理所知范围的 Sidecar 资源、拦下未知目的地的 REGISTRY_ONLY、把外部目的地登记到服务注册表的 ServiceEntry,以及把出站路径汇聚到一处的出口网关(egress gateway)。四者拦截的对象各不相同,没有哪一个能单独拦住全部。
为什么需要它
sidecar 默认知道集群中的所有服务。服务有几千个时,每个代理都要持有几千个集群,每当有一个服务变化,istiod 就要把它推送给所有代理。而且注册表中没有的目的地(外部地址、Pod IP)会被 PassthroughCluster 直接放走——网格并不知道谁在往哪里出站。
安全要求的方向则相反。比如“只有支付服务能访问信用卡公司的 API”“出站路径只有一处可供审计”。网格要想帮上忙,首先要按名称认识外部目的地,然后把出站的节点汇聚到一处。
工作原理
Sidecar 资源——规定这个命名空间的代理知道什么。
egress:
- hosts: ["./*", "istio-system/*"] ← 같은 네임스페이스와 istio-system 만 안다
outboundTrafficPolicy:
mode: REGISTRY_ONLY ← 모르는 곳은 BlackHoleCluster(502)
缩小范围后,代理配置会变小,再加上 REGISTRY_ONLY,范围之外的就被拦下。被拦下的请求会在访问日志中留下路由名称 block_all,状态码为 502。
ServiceEntry——把外部目的地登记到注册表。location: MESH_EXTERNAL 表示“在网格之外,不要加 mTLS”,resolution: DNS 表示用 DNS 解析端点名称。不过主机名本身(api.partner.test)在集群 DNS 中并不存在。开启 sidecar 的 DNS 代理(ISTIO_META_DNS_CAPTURE)后,sidecar 会为该名称自动分配 240.240.0.0/16 网段的虚拟 IP 来应答,这个值也会写入 ServiceEntry 的 status.addresses。
出口网关——把出站路径汇聚到一处。一个 VirtualService 负责两个区段。
| 区段 | 匹配 | 发往 |
|---|---|---|
| sidecar → 网关 | gateways: [mesh] |
istio-egressgateway(mTLS) |
| 网关 → 外部 | gateways: [partner-egress] |
真正的目的地 |
从 sidecar 到网关的 mTLS 不会自动建立。要把 Gateway 服务器设为 HTTPS、tls.mode: ISTIO_MUTUAL,并在 sidecar 前往网关时使用的 DestinationRule 子集中指定 ISTIO_MUTUAL 和 sni(官方文档 Egress Gateways with TLS Origination 使用同样的组合)。该 DestinationRule 按客户端命名空间 → 服务命名空间 → 根命名空间的顺序查找,所以要让多个命名空间共用同一个网关,就把它放在网关一侧(istio-system)。这样通过 mTLS 连起来后,网关就知道发出请求的工作负载的身份。因此在网关上设置授权策略,就能在一处决定“谁可以出站到这个外部目的地”。
拦不住的情况。Sidecar 和 REGISTRY_ONLY 只是该命名空间代理的配置。其他命名空间的代理仍然是 ALLOW_ANY,没有 sidecar 的 Pod 则根本不知道这些配置。Istio 文档指出,仅靠出口网关无法强制所有外部流量都经过它,必须用 Kubernetes 网络策略之类的其他手段堵住网关之外的路径。
在现场相遇的样子
“开启 REGISTRY_ONLY 后,指标采集中断了”——Pod 原本访问的外部地址(监控 SaaS、云元数据等)没有登记在注册表中。正确的顺序是:开启之前,先在访问日志中统计经 PassthroughCluster 出站的请求,并用 ServiceEntry 登记好。
“建了出口网关,合作方防火墙日志里记录的却是 Pod IP”——VirtualService 缺少 mesh 一侧的规则,或者主机不一致,导致 sidecar 没有经过网关而是直接出站。接收方看到的来源是最诚实的证据。
“网关上拦了,有的团队却仍然能出去”——该团队的命名空间没有 Sidecar 配置,或者是没有 sidecar 的 Pod。网关只能拦住经过它的请求。
官方文档:Accessing External Services、Egress Gateways、Sidecar、DNS Proxying
下一项实验要做什么
在网格之外放一个扮演合作方 API 的 nginx,先统计默认值下去哪儿都能出站的情况。用 Sidecar 缩小范围,用 REGISTRY_ONLY 拦截,再用 ServiceEntry 只登记合作方,然后用出口网关汇聚路径,并以合作方看到的来源来确认。最后看到在其他命名空间中仍然存在绕过网关的路径,再用网关的授权策略缩小可以出站的身份。