Kubernetes v1.36:弃用并移除 Service ExternalIPs

Service 的 .spec.externalIPs 字段,是早期为非云环境集群提供类似云负载均衡器功能的一次尝试。遗憾的是,这个 API 假设集群里的每个用户都完全可信。只要这一前提不成立,它就会带来多种安全漏洞,具体见 CVE-2020-8554。

从 Kubernetes 1.21 开始,项目就建议所有用户禁用 .spec.externalIPs。为了便于执行,Kubernetes 还加入了可启用的准入控制器 DenyServiceExternalIPs。当时,SIG Network 认为默认阻止这一功能会造成过大的破坏性变更,因此没有采用这一做法。

然而,安全问题始终存在。作为一个项目,我们越来越难以接受这个功能“默认不安全”的状态。此外,现在希望获得类似负载均衡功能的非云集群,已经有了几种更好的替代方案。

因此,Kubernetes 1.36 正式弃用了 Service 的 .spec.externalIPs 字段。我们预计,未来某个 Kubernetes 次版本会从 kube-proxy 中移除对这一行为的实现,并更新 Kubernetes 的一致性认证标准,要求符合标准的实现不再提供此项支持。

术语说明:哪些内容没有被弃用

“external IP”这个说法在 Kubernetes 中有多种含义:

  • Service API 的 .spec.externalIPs 字段,可为 Service 添加额外的响应 IP 地址。
  • Node API 的 .status.addresses 字段,可以列出多种地址类型,其中一种叫做 ExternalIP。
  • kubectl 按默认格式显示 LoadBalancer 类型 Service 的信息时,会把负载均衡器 IP 地址放在 EXTERNAL-IP 列下面。

这次弃用仅针对第一项。如果你的任何 Service 都没有设置 externalIPs 字段,就不受这次弃用影响。

不过,作为预防措施,你仍然可以考虑启用 DenyServiceExternalIPs 准入控制器,阻止今后使用 externalIPs 字段。

externalIPs 的替代方案

如果你正在使用 .spec.externalIPs,可以考虑下面几种替代方案。先来看这样一个 Service:

apiVersion: v1
kind: Service
metadata:
  name: my-example-service
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: my-example-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  externalIPs:
    - "192.0.2.4"

使用手动管理的 LoadBalancer Service

最容易、但也是最差的方案,是把使用 externalIPs 的 Service 改为 type: LoadBalancer,再手动分配一个负载均衡器 IP。它本质上与 externalIPs 完全相同,只有一个重要区别:负载均衡器 IP 属于 Service 的 .status,而非 .spec。在启用了 RBAC 的集群中,普通用户默认无法编辑它。

因此,只有管理员授予相应权限的用户,才能使用这种替代方案。不过,这些用户随后也就拥有了复现 CVE-2020-8554 的全部能力;仍然没有进一步的检查,来确保一个用户不会窃取另一个用户的 IP 等资源。

由于 Kubernetes 中 .status 的工作方式,你必须先创建不带负载均衡器 IP 的 Service,再在第二步添加 IP:

文件:loadbalancer-service.yaml 与相关命令

$ cat loadbalancer-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-example-service
spec:
  # prevent any real load balancer controllers from managing this service
  # by using a non-existent loadBalancerClass
  loadBalancerClass: non-existent-class
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: my-example-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
$ kubectl apply -f loadbalancer-service.yaml
service/my-example-service created
$ kubectl patch service my-example-service --subresource=status --type=merge -p '{"status":{"loadBalancer":{"ingress":[{"ip":"192.0.2.4"}]}}}'

示例中的注释说明:使用一个不存在的 loadBalancerClass,防止真正的负载均衡控制器接管该 Service。

使用非云负载均衡控制器

尽管 LoadBalancer Service 最初是为云负载均衡器设计的,Kubernetes 也可以通过 MetalLB 这样的第三方负载均衡控制器,在非云平台上支持它们。这解决了 externalIPs 相关的安全问题:管理员可以配置控制器允许分配给 Service 的 IP 地址范围,控制器也会确保两个 Service 不会同时使用同一个 IP。

例如,在安装并配置 MetalLB 后,集群管理员可以设置一个供集群使用的 IP 地址池:

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: production
  namespace: metallb-system
spec:
  addresses:
  - 192.0.2.0/24
  autoAssign: true
  avoidBuggyIPs: false

此后,用户可以创建 type: LoadBalancer 的 Service,IP 地址分配由 MetalLB 处理。为了与 externalIPs 的用法保持向后兼容,MetalLB 甚至支持 Service 中已经弃用的 loadBalancerIP 字段。因此,终端用户可以请求一个特定 IP——前提是该地址可用——而不是让控制器随机分配:

apiVersion: v1
kind: Service
metadata:
  name: my-example-service
spec:
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: my-example-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  loadBalancerIP: "192.0.2.4"

其他负载均衡控制器也可以采用类似方式。这种方案让集群管理员而非用户,控制哪些 IP 地址可以被分配。

使用 Gateway API

另一种可能的解决方案,是使用 Gateway API 的某个实现。

Gateway API 允许集群管理员定义 Gateway 资源,并通过 .spec.addresses 字段给它关联 IP 地址。由于 Gateway 资源本来就是由集群管理员管理的,可以通过 RBAC 规则,仅允许具有相应权限的用户管理它们。

下面是一个可能的配置示例:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
spec:
  gatewayClassName: example-gateway-class
  addresses:
  - type: IPAddress
    value: "192.0.2.4"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-route
spec:
  parentRefs:
  - name: example-gateway
  rules:
  - backendRefs:
    - name: example-svc
      port: 80
---
apiVersion: v1
kind: Service
metadata:
  name: example-svc
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: example-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

Gateway API 项目是 Kubernetes 中下一代 Ingress、负载均衡和服务网格 API。它旨在解决 Service 和 Ingress 资源的不足,是一种可靠、稳健且仍在积极开发的方案。

externalIPs 的弃用时间表

这次弃用的大致时间表如下:

  1. Kubernetes 1.36 发布时,该字段被弃用;用户使用这一字段时,Kubernetes 现在会发出警告。
  2. 大约一年后,最早在 v1.40,kube-proxy 将默认禁用 .spec.externalIPs 支持。不过,如果用户需要更多迁移时间,仍有办法重新启用。
  3. 再过大约一年,最早在 v1.43,将彻底禁用这一支持,用户也无法重新启用。

原文:Kubernetes v1.36: Deprecation and removal of Service ExternalIPs,作者 Adrian Moisey(independent)、Dan Winship(Red Hat),2026年5月14日。Kubernetes 网站内容采用 CC BY 4.0;许可来源。本页为中文翻译,代码保留原文。版本与迁移时间表按原文保留,不表示后续计划已经实施。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容