Kubernetes v1.36 正式废弃 Service externalIPs:安全隐患终将清除

2026-05-15 34 预计阅读时间:1 分钟
来源:kubernetes.io AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:8 分钟

Kubernetes Service 的 .spec.externalIPs 字段,从 1.21 起就被官方建议禁用,但一直没敢动手——因为怕破坏现有集群。五年后,v1.36 终于把这个"默认不安全"的功能正式标记为废弃,并给出了明确的移除时间线。如果你还在用这个字段,现在就是迁移的最后窗口。

为什么非废不可

externalIPs 的设计初衷很简单:在没有云厂商 LoadBalancer 的裸金属集群里,让 Service 能响应一个额外的外部 IP。比如:

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"

看起来方便,但问题在于:任何有 Service 创建权限的用户都能随意声明任意 IP。没有校验、没有冲突检测、没有管理员管控。这意味着集群里的普通用户可以:

  • 把别人的 Service IP 劫持到自己手上
  • 把节点 IP、网关 IP 甚至集群外系统的 IP 挪用过来
  • 实现中间人攻击或流量窃取

这就是 CVE-2020-8554 描述的攻击场景。在多租户或权限未严格隔离的集群里,这是一个真实可利用的漏洞。

别混淆:这次只废 .spec.externalIPs

Kubernetes 里 "External IP" 这个词出现在好几个地方,容易搞混:

位置 含义 是否废弃
Service .spec.externalIPs 让 Service 响应额外 IP ✅ 本次废弃
Node .status.addresses 中 type=ExternalIP 节点的外部地址 ❌ 不受影响
kubectl get svc 的 EXTERNAL-IP 列 LoadBalancer 类型 Service 的 LB 入口 IP ❌ 不受影响

简单判断标准:如果你没有在任何 Service 里设置 externalIPs 字段,这件事跟你无关。但作为预防措施,建议仍然启用 DenyServiceExternalIPs 准入控制器,防止未来有人误用。

三条替代路径

1. 手动管理 LoadBalancer Service——最简单但最不推荐

externalIPs 换成 type: LoadBalancer,然后手动 patch .status.loadBalancer.ingress 写入 IP。关键区别:.status 在 RBAC 开启时普通用户默认改不了,所以管理员可以控制谁有权限。

但本质上,安全模型没变——拿到权限的用户照样能劫持 IP,只是门槛高了一点。

操作步骤:

# 第一步:创建 Service(不带 LB IP)
cat loadbalancer-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-example-service
spec:
  # 用一个不存在的 loadBalancerClass,阻止真实 LB 控制器接管
  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

# 第二步:patch status 写入 IP
kubectl patch service my-example-service --subresource=status \
  --type=merge -p '{"status":{"loadBalancer":{"ingress":[{"ip":"192.0.2.4"}]}}}'

注意 --subresource=status 这个参数——Kubernetes 1.24+ 才支持对 status 子资源的独立 patch,老版本集群需要确认 API 支持。

这条路径只适合临时过渡,长期方案看下面两条。

2. 第三方 LoadBalancer 控制器——推荐方案

MetalLB 是最成熟的选择。管理员定义 IP 池,控制器自动分配、保证不冲突、保证权限可控。

先安装 MetalLB(以 Helm 为例):

helm repo add metallb https://metallb.github.io/metallb
helm install metallb metallb/metallb -n metallb-system --create-namespace

然后配置 IP 池:

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

用户创建普通 LoadBalancer Service,MetalLB 自动从池里分配 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

如果需要指定 IP(兼容旧 externalIPs 的使用习惯),MetalLB 还支持已废弃的 loadBalancerIP 字段做请求式分配——只要该 IP 在池内且未被占用:

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

核心优势:管理员控制 IP 范围,控制器保证唯一性,用户不能越界。OpenELB、PureLB 等其他控制器思路类似。

3. Gateway API——面向未来的方案

Gateway API 是 Kubernetes Ingress / Load Balancing / Service Mesh 的下一代标准。管理员创建 Gateway 资源并绑定 IP,用户只创建 Route 挂到已有 Gateway 上——权限分离天然到位。

# 管理员创建 Gateway(绑定外部 IP)
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"
# 用户创建路由(无需知道外部 IP)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-route
spec:
  parentRefs:
    - name: example-gateway
  rules:
    - backendRefs:
        - name: example-svc
          port: 80
# 后端仍然是普通 ClusterIP Service
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 仍在快速演进,生态实现(如 Istio、Envoy Gateway、Traefik)已经可用。如果你的集群已经在用或计划引入 Service Mesh / Ingress 重构,这是最干净的路径。

废弃时间线

版本 动作
v1.36(现在) .spec.externalIPs 正式标记废弃,使用时 API Server 会发出警告
v1.40+(约一年后) kube-proxy 默认停止实现 externalIPs 行为,但提供 opt-in 机制允许临时回开
v1.43+(约两年后) 完全移除,不可回开,一致性测试要求实现不得支持该字段

时间线是"最早"版本号,实际可能顺延,但方向不会变。

迁移检查清单

在 v1.36 发布后,建议立即做以下检查:

# 1. 搜索集群中所有使用了 externalIPs 的 Service
kubectl get svc -A -o json | \
  jq -r '.items[] | select(.spec.externalIPs != null and .spec.externalIPs | length > 0) | "\(.metadata.namespace)/\(.metadata.name): \(.spec.externalIPs)"'

# 2. 启用准入控制器,阻止新的 externalIPs 创建(kube-apiserver 启动参数)
# --enable-admission-plugins=DenyServiceExternalIPs

# 3. 如果搜索结果为空——恭喜,你什么都不用改,只需启用准入控制器做防御
# 4. 如果有结果——逐条评估,选择 MetalLB 或 Gateway API 替代

externalIPs 从"不安全但舍不得删"到"正式废弃",走了五年。剩下的迁移窗口大概还有两年。现在查一遍集群,比将来 kube-proxy 突然不转发流量时要从容得多。


相关推荐