1111 字
6 分钟
以为有网络隔离的集群,实查 0 条规则

一条 NetworkPolicy 都没有的集群,和规则写齐的集群,业务跑起来看不出区别。我的判断是:安全配置没有默认生效这回事,只能自己一条条去查;而查出缺口去堵的时候,那条路上还跑着控制面自己的流量。

三条进入沙箱的路径与各自的拦截点

查下来是 0 条#

一个沙箱服务的生产集群,文档和会议上都按「有网络隔离」处理。实际查出来是 0 条 NetworkPolicy。沙箱能直连共享盘,能访问集群内部服务。

同一次检查里还有一条:单台机器的 Pod 上限,会议纪要写 150,实际是 110。

两条错得不一样,一条把数写大了,一条把没有的东西当成有。但都是从「应该是这样」推出来的,没人去读一遍实际值。

NetworkPolicy 这条更难发现,因为它的默认语义是放行:一个 Pod 没有被任何策略选中时,进出不受限制。这和「策略写对了、业务流量恰好被放行」从外面看是一样的。Pod 正常跑,接口正常通,监控没有异常。缺口不会自己报出来。

想把这两种状态分开,只能去数有几条规则、选中了哪些 Pod。

同一台机器上的程序不受 NetworkPolicy 管#

实验集群上测出另一件事:宿主机上的程序能不经授权换掉沙箱的访问凭据,直接接管本机上的沙箱。NetworkPolicy 没拦住。

原因在它的执行位置。NetworkPolicy 由 CNI 在 Pod 网络数据面上执行,管的是进出 Pod 网络命名空间的流量。宿主访问本机 Pod 走的是节点上的本地路由,不经过 CNI 的那个执行点。用 hostNetwork 的 Pod 也一样——它共用宿主的网络命名空间,按 Pod 选择器写的策略选不中它。

同一个坑我在另一个组件上踩过第二次。我自己写的文档里记着「应用层密钥加网络层 NetworkPolicy 两层防护,任一层失效另一层仍在」。实测网络层这一层在这个集群根本没生效:那个组件用 hostNetwork,流量到的是宿主网络栈。实际只有密钥一层,好在它顶住了全部尝试。

要在网络层限制一个 hostNetwork 端口,只能用宿主机防火墙,NetworkPolicy 做不到。

堵住它,kubelet 的探针跟着被堵#

补救办法是每台机器加一条防火墙规则,挡住宿主和共用宿主网络的 Pod 连沙箱内 execd 的端口。实测加上之后,从宿主连 execd 立即被拒;普通 Pod 不受影响,那条路还得靠 NetworkPolicy 挡。

kubelet 也在被挡的那一侧。它跑在宿主上,TCP 就绪探针发起的位置正好被这条规则挡住:Pod 起来了,探针连不上,就绪判定一直不过。

就绪检查只能换成 exec probe,在容器里跑一条命令。exec 走的是 API Server 到 kubelet、在 Pod 的命名空间里 fork 一个进程,不经过 Pod 的网络接口,所以防火墙和 NetworkPolicy 都管不到它。

这不是等价替换。TCP 探针验的是「端口在监听」,exec probe 验的是你写的那条命令,判据要重新定;每次探测还多一次进程启动。这一步当时只定了方向,没在集群上实测过。

默认不通,再按需放行#

补几条规则不解决问题,得把默认值反过来:沙箱网络默认不通,只放行明确需要的出网。

要放行的有三类:同一任务的几个沙箱互访、模型服务、判分服务。要堵的是内网网段、共享盘,还有集群自己的公网入口。

最后一条容易漏。沙箱绕到公网、再从集群自己的入口兜回来,等于绕开了全部内部策略。

这些地址必须自动发现,不能写死。写死一个会变的 IP,和「缓存了 Pod IP、Pod 重启换了 IP」是同一类错误:规则还在,已经不匹配了,而且不报错。

默认拒绝的代价是每个新需求都变成一次变更申请。这笔代价我认:漏放行一条,业务立刻报错;漏拦截一条,谁都不知道。