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 的弃用时间表
这次弃用的大致时间表如下:
- Kubernetes 1.36 发布时,该字段被弃用;用户使用这一字段时,Kubernetes 现在会发出警告。
- 大约一年后,最早在 v1.40,
kube-proxy将默认禁用.spec.externalIPs支持。不过,如果用户需要更多迁移时间,仍有办法重新启用。 - 再过大约一年,最早在 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;许可来源。本页为中文翻译,代码保留原文。版本与迁移时间表按原文保留,不表示后续计划已经实施。











暂无评论内容