你把办公室 VPN 的公网地址加入 AKS API 服务器的允许列表,kubectl 就能工作。一周后,某个节点池扩容失败,某个 CI 任务无法访问集群,门户显示的错误却只字未提 IP 范围。没有人改过允许列表。你启用的控制是 API 服务器前面的公网 IP 过滤器,它不知道调用者是谁,只知道调用来自哪个地址。VPN 只是集群所需的多个地址之一,而且很少是第一个出问题的那个。

允许列表匹配的是地址,不是人

--api-server-authorized-ip-ranges 它接受的是 CIDR 网段,而不是用户或组。当开发者的请求离开 VPN 服务器前往 Azure 时,API 服务器看到的源地址就是 VPN 服务器的公网出口。因此该 VPN 上的每个人都共享同一条记录,而 VPN 之外的人在认证发生之前就会被拒绝。

这也意味着这条记录会放进比你预想更多的人。拥有 VPN 访问权限的外包人员、同一台集中器上的个人笔记本,以及被盗用的账号,全都来自同一个地址。集群授权没有改变,因此基于角色的访问控制仍然决定调用者能做什么,但允许列表不是身份控制,也绝不应该被这样描述。

最先出问题的通常不是 kubectl

允许列表作用于 API 服务器的公网端点,因此你列出的地址之外的一切都会立即停止工作。明显的受害者是没有 VPN 路由的第二个办公室、托管流水线中的 CI 运行器,以及忘记连接的笔记本。这些很容易发现,因为有人在等。

不那么明显的是由 Azure 或你自己的工具驱动的自动化。某些平台发起的操作会从并非你 VPN 的地址访问 API 服务器,当其中一个在列表收紧后立刻开始失败时,时间点就是线索。不要因为交互式访问可用就认为列表完整;在断定集群坏了之前,先查清该功能对你的区域和订阅有什么要求。

失败特征很有帮助。如果请求根本没到认证环节,那就是过滤器。如果通过了认证随后收到权限错误,说明过滤器已经尽责,问题出在基于角色的访问控制上,而允许列表从不涉及这一层。

用一条命令构建完整列表

更新参数会替换整个列表,而不是追加一条记录,因此只带 VPN 地址的命令会悄悄丢掉原有的其他内容。先读取当前值,保留它,然后发送并集:

az aks show -g rg-app -n aks-prod \
  --query apiServerAccessProfile.authorizedIpRanges -o tsv

az aks update -g rg-app -n aks-prod \
  --api-server-authorized-ip-ranges 203.0.113.10/32,198.51.100.0/24

在依赖这一点之前,请为 VPN 服务器使用预留的公网 IP。动态地址会在重连时变化,而每一次变化都会悄悄把团队锁在集群之外,直到有人从仍然被允许的网络更新列表。如果你有两个 VPN 出口做冗余,就都列上,因为过滤器没有首选路径的概念。

把变更顺序安排好,让第一次被允许的尝试本身就是测试。先公布新列表,再应用它,然后立刻从 VPN 连接;顺序反过来就会留下一段时间,没人能访问 API 服务器去修 CIDR 网段里的笔误。

允许列表从未覆盖的东西

它不会让 API 服务器变成私有。私有集群使用私有端点和私有 DNS,而授权范围列表并不是替代它的控制。它也不会改变被放行的调用者能做什么,因此泄露的管理员 kubeconfig 仍然能从任何被允许的地址使用。把凭据当作安全边界,把列表当作网络便利。

它与工作负载毫无关系。列表启用时 Pods 和 Services 照常运行,而且通过 VPN 访问应用是另一个问题。只有集群 IP 的 Service 仍然需要内部负载均衡器、ingress 或 port-forward,笔记本才能打开它。把这两种设计混在一起,会产生一张声称限制开发者、实际上对应用毫无限制的变更单。

从两侧验证,然后执行一次真实操作

用 VPN 上的新客户端和 VPN 外部的客户端分别测试。外部尝试应当在 API 服务器被拒绝,而不是在名称解析处被拒绝;如果名称无法解析,你测的是 DNS,对过滤器一无所知。然后执行一次非交互式 kubectl 调用的操作,例如节点池扩容或升级检查,并确认它确实完成,而不是想当然。

把之前的列表值记录在变更旁边,并在更新后重新读取实际生效的值,而不是相信输入过的命令。AKS 授权 IP 范围文档列出了支持的参数以及应放入同一列表的平台要求,而这正是大多数变更单描述所遗漏的部分。

相关文章