Kubernetes v1.36:Service ExternalIPs 的弃用与移除

来源: Kubernetes Blog
原文链接:https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/


.spec.externalIPs 字段用于 Service,最初是为了给非云集群提供类似云负载均衡器的功能。遗憾的是,该 API 假设集群中的每个用户都完全可信,而在任何不满足此假设的情况下,它都会引发多种安全漏洞,如 CVE-2020-8554 所述。

自 Kubernetes 1.21 起,Kubernetes 项目已建议所有用户禁用 .spec.externalIPs。为简化操作,Kubernetes 还新增了一个可启用的准入控制器(DenyServiceExternalIPs)来实现此目的。当时,SIG Network 认为默认禁用该功能会带来过大的破坏性变更,因此未予采纳。

然而,安全问题依然存在,作为项目方,我们对该功能“默认不安全”的状态越来越不满。此外,对于希望获得类似负载均衡器功能的非云集群,现在已有多种更好的替代方案。

因此,Service 的 .spec.externalIPs 字段现已在 Kubernetes 1.36 中正式弃用。我们预计未来 Kubernetes 的次要版本将从 kube-proxy 中移除该行为的实现,并更新 Kubernetes 一致性标准,要求符合标准的实现不得提供支持。

关于术语的说明,以及哪些内容未被弃用

在 Kubernetes 中,“外部 IP”这一术语有些过载:

  • Service API 的 .spec.externalIPs 字段可用于添加 Service 将响应的额外 IP 地址。
  • Node API 的 .status.addresses 字段可以列出多种不同类型的地址,其中一种称为 ExternalIP
  • kubectl 工具在默认输出格式下显示 LoadBalancer 类型的 Service 信息时,会在 EXTERNAL-IP 列标题下显示负载均衡器 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 服务替代 externalIPs

最简单(但也是最差)的选项是直接放弃使用 externalIPs,转而使用 type: LoadBalancer 服务,并手动分配一个负载均衡器 IP。本质上,这与 externalIPs 完全相同,但有一个重要区别:负载均衡器 IP 属于服务的 .status 而非 .spec,在启用了 RBAC 的集群中,普通用户默认无法编辑它。因此,这种替代 externalIPs 的方式仅对管理员授予权限的用户可用(尽管这些用户将完全有能力复制 CVE-2020-8554;仍然没有任何进一步的检查来确保一个用户不会窃取另一个用户的 IP 等)。

由于 Kubernetes 中 .status 的工作方式,你必须先创建不带负载均衡器 IP 的服务,然后再将 IP 添加为第二步操作:

$ 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"}]}}}'

使用非云负载均衡器控制器

虽然 LoadBalancer 服务最初设计为由云负载均衡器(cloud load balancer)支持,但 Kubernetes 也可以通过使用第三方负载均衡器控制器(如 MetalLB)在非云平台上支持它们。这解决了与 externalIPs 相关的安全问题,因为管理员可以配置控制器将哪些 IP 地址范围分配给服务,并且控制器会确保两个服务不会使用相同的 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(服务),MetalLB 将负责 IP 地址的分配。MetalLB 甚至支持 Service 中已弃用的 loadBalancerIP 字段,因此最终用户可以请求一个特定的 IP(假设该 IP 可用),以实现与 externalIPs 方法的向后兼容,而不是被随机分配一个 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。Gateway API 旨在解决 Service 和 Ingress 资源的缺陷,使其成为一个非常可靠且稳健的解决方案,目前正处于积极开发中。

externalIPs 弃用时间线

该弃用的大致时间线如下:

  1. 随着 Kubernetes 1.36 的发布,该字段已被弃用;当用户使用此字段时,Kubernetes 现在会发出警告
  2. 大约一年后(最早在 v1.40),kube-proxy 中将禁用对 .spec.externalIPs 的支持,但用户仍可选择重新启用,以便有更多时间迁移。
  3. 再过大约一年(最早在 v1.43),支持将被完全禁用;用户将无法再选择重新启用。