Why externalIPs is being removed from the default path
The field was always a trust gamble. If a user can edit a Service and point externalIPs at an address, kube-proxy will happily program packet handling for it. That works until two users pick the same IP, or one user claims an address already in use elsewhere. The cluster has no built-in way to prove ownership, so the model relies on everyone being trusted not to do something daft.
That gap is the problem behind CVE-2020-8554. Kubernetes has treated externalIPs as insecure by default since 1.21, but the field still sat there because removing it outright would have broken too many clusters at once. Kubernetes 1.36 is the point where that patience runs out. The API now warns when externalIPs is used, and later releases are expected to remove the behaviour from kube-proxy. Conformance will also move so that implementations are not expected to support it.
The security hole behind arbitrary IP claims
externalIPs lets a user describe routing behaviour for an address they do not control. That is a poor fit for multi-user clusters, and a worse fit for anything with even mild separation between tenants. It only takes one Service manifest to hijack traffic that should have gone somewhere else.
The awkward part is that the old model never checked intent against ownership. There is no admission step, no address registry, and no later guardrail in the data plane. If the API server accepts the object, kube-proxy does the rest. That is fine for lab clusters where one person owns the lot. It is less charming in shared environments.
What Kubernetes 1.36 changes before the old behaviour disappears
Kubernetes 1.36 introduces deprecation warnings for Services that still use externalIPs. That gives clusters a clean signal before removal lands in the data plane. The next step is a future minor release, no earlier than 1.40, where support is expected to disappear from kube-proxy.
That matters because the breakage will not be subtle. Once support goes, the field stops doing anything useful in clusters that relied on it for traffic handling. Any migration plan that waits for removal has already waited too long. The safe move is to treat 1.36 as the cut-over warning, not as a gentle heads-up for later.
Safer ways to hand out addresses without reopening the same problem
The replacement pattern is simple: move address assignment out of ordinary Service spec fields and into something a controller or admin owns. That keeps users from naming arbitrary IPs while still letting them expose traffic.
Use LoadBalancer Services when the controller owns the IP
A LoadBalancer Service is the nearest direct replacement when an external controller owns the address pool. The point is not the Service type itself, but who assigns the address. If a load balancer controller manages the pool, the user asks for exposure and the controller decides which IP to attach.
Kubernetes also allows loadBalancerIP in some setups, but that only stays safe if the controller owns the allocation path. If users can patch the status subresource directly, they can recreate the same trust problem with a different label on it. The cleaner pattern is a controller that writes the address into Service status, while ordinary users only create or edit the Service spec. The status field is where the result belongs, not the request.
Put the address in Service status, or move to MetalLB and Gateway API
If the cluster needs fixed addresses, Service status is the safer place for the final IP. The controller can assign status.loadBalancer.ingress[].ip, and users never get to claim the address in the spec. A manual patch works too, but only for admins who already have the rights to do damage. That is not a flaw in the design so much as a reminder that RBAC is not decorative.
MetalLB follows the same basic idea with a clearer allocation model. You define an IPAddressPool in metallb-system, set the address range, and let MetalLB assign from that pool. The addresses are not guessed by users and are not scattered through Service specs like confetti. For example, an IPAddressPool can cover 192.0.2.0/24 with automatic assignment enabled, and a Service can request a specific loadBalancerIP when the controller allows it.
Gateway API gives a similar control point with more structure around exposure. A Gateway can bind to an address through .spec.addresses, and HTTPRoute attaches to that Gateway through parentRefs. The important part is that the IP lives with the Gateway resource, not in some loose Service field where anyone with edit rights can poke at it. For ingress-style routing, that is a better boundary than pretending every user should be trusted with the front door key.
Block new externalIPs with DenyServiceExternalIPs before the cut-over
The DenyServiceExternalIPs admission controller is the blunt instrument that stops new use before removal lands. It rejects Services that still set externalIPs, which is exactly what an admission controller should do when the field is the problem. Turn it on before migration, not after the first broken rollout.
That leaves time to find the Services still relying on the field, move them to a LoadBalancer model, or swap in MetalLB or Gateway API where the address assignment is controller-owned. If the cluster has mixed ownership and nobody can agree who should control what, that is usually the point where a migration plan becomes a Friday evening.




