中文内容
Kubernetes v1.36:弃用并移除 Service ExternalIPs
Service 的 .spec.externalIPs 字段是早期为非云集群提供类似云负载均衡器功能的一种尝试。遗憾的是,该 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 中,“external 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 Services 替代 externalIPs
最简单(但也是最差)的选择,是直接从使用 externalIPs 改为使用 type: LoadBalancer 服务,并手动分配一个负载均衡器 IP。本质上,这几乎与 externalIPs 完全相同,但有一个重要区别:负载均衡器 IP 是 Service 的 .status 的一部分,而不是其 .spec 的一部分;在启用了 RBAC 的集群中,普通用户默认无法编辑它。因此,这种 externalIPs 的替代方式只会提供给获得管理员授权的用户(不过这些用户随后将完全有能力复制 CVE-2020-8554;仍然不会有进一步检查来确保一个用户没有窃取另一个用户的 IP 等)。
由于 Kubernetes 中 .status 的工作方式,你必须先创建不带负载均衡器 IP 的 Service,然后在第二步添加该 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 服务最初设计为由云负载均衡器作为后端支持,但 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 字段,因此终端用户可以为与 externalIPs 方法保持向后兼容而请求一个特定 IP(假设该 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 内部 Kubernetes Ingress、负载均衡和 Service Mesh API 的下一代方案。Gateway API 旨在修复 Service 和 Ingress 资源的不足,使其成为一个非常可靠、稳健且正在积极开发的解决方案。
externalIPs 弃用时间线
此次弃用的大致时间线如下:
- 随着 Kubernetes 1.36 发布,该字段已被弃用;当用户使用此字段时,Kubernetes 现在会发出警告
- 大约一年后(最早 v1.40),kube-proxy 将禁用对 .spec.externalIPs 的支持,但如果用户需要更多时间完成迁移,将有一种方式可以选择重新启用
- 再大约一年后(最早 v1.43),支持将被完全禁用;用户将无法再选择重新启用
- 下一页 →