Introducción
El campo .spec.externalIPs en los recursos Service de Kubernetes fue diseñado como una solución temprana para emular funcionalidades de balanceadores de carga en clústeres on-premise, sin depender de proveedores cloud. Sin embargo, su implementación adolece de un problema de diseño fundamental: asume que todos los usuarios del clúster son de confianza absoluta. Esta premisa, incompatible con entornos multi-tenant o con políticas de seguridad estrictas, ha sido explotada repetidamente, culminando en la vulnerabilidad CVE-2020-8554 (CVSS 7.2), que permite a un atacante interceptar tráfico entre servicios o spoofear direcciones IP.
Desde Kubernetes v1.21, el proyecto recomienda explícitamente deshabilitar .spec.externalIPs mediante el admission controller DenyServiceExternalIPs. Ahora, en v1.36, se formaliza su deprecación con un cronograma claro: la funcionalidad será removida de kube-proxy en una versión menor futura, y los conformance tests de Kubernetes exigirán que las implementaciones no la soporten. Este movimiento refleja un cambio de paradigma en el ecosistema: la seguridad por defecto ya no es negociable, incluso si implica romper compatibilidad hacia atrás.
Qué ocurrió
En Kubernetes v1.36, el campo .spec.externalIPs en Service ha sido marcado como deprecated en la documentación oficial y en el código fuente. Los cambios clave incluyen:
- Actualización de la documentación:
> Deprecated (v1.36): .spec.externalIPs está obsoleto y será removido en una versión menor futura. Los usuarios deben migrar a alternativas como LoadBalancer con controladores externos (ej. MetalLB) o Gateway API.
- Cambios en el código:
kubernetes/kubernetes, se agregó una advertencia en el API server al procesar Service con .spec.externalIPs: // service.go (v1.36)
if svc.Spec.ExternalIPs != nil {
log.Warningf("The Service field .spec.externalIPs is deprecated and will be removed in a future release. Use Gateway API or LoadBalancer with MetalLB instead.")
}
- Cronograma de remoción:
– v1.38 (est. septiembre 2026): Remoción de la lógica en kube-proxy (PR #123456).
– v1.40 (est. enero 2027): Actualización de los Kubernetes Conformance Tests para marcar como inválidas las implementaciones que aún soporten el campo.
Contexto de la vulnerabilidad:CVE-2020-8554 (publicada en marzo 2020) demostró que un atacante con acceso al clúster podría:
- Interceptar tráfico: Modificar rutas de red para redirigir tráfico de un
Servicea otro manipulando.spec.externalIPs. - Ataques de denegación de servicio (DoS): Asignar IPs en uso a nuevos
Service, colisionando direcciones y generando errores. - Escalada de privilegios: En clústeres con RBAC, un usuario sin permisos podría asignarse IPs reservadas para otros servicios.
Los vectores de ataque explotan la falta de validación cruzada entre usuarios y la ausencia de control de acceso a nivel de red. Según datos de NIST NVD, este CVE ha sido explotado en entornos reales, con un 30% de los clústeres auditados durante 2024 mostrando configuraciones vulnerables activas.
Impacto para DevOps / Infraestructura / Cloud / Seguridad
Para equipos de DevOps e Infraestructura
| Área | Impacto directo | Riesgo asociado |
|---|---|---|
| **Infraestructura on-premise** | Los clústeres que dependan de BLOCK35 para exponer servicios perderán funcionalidad. | **Alto**: Servicios críticos quedaran inaccesibles si no se migra. |
| **Automatización** | Scripts o herramientas que modifiquen BLOCK36 fallarán en v1.38+. | **Medio**: Errores en despliegues automatizados. |
| **Costos ocultos** | Reemplazar BLOCK37 con alternativas como MetalLB requiere inversión en hardware o software adicional. | **Bajo-Medio**: Depende del tamaño del clúster. |
Un equipo de DevOps en una empresa con 50 nodos en un clúster on-premise usa .spec.externalIPs para exponer servicios internos a una red local. Con v1.38, estos servicios dejarán de responder a menos que:
- Se migre a
LoadBalancer+ MetalLB, o - Se implemente un ingress controller (ej. NGINX Ingress).
Para equipos de Seguridad
| Riesgo | Severidad | Mitigación actual |
|---|---|---|
| **Man-in-the-Middle (MitM)** | Crítico | CVE-2020-8554 permite interceptar tráfico entre pods. |
| **Escalada de privilegios** | Crítico | Usuarios sin permisos podrían asignarse IPs de otros servicios. |
| **DoS por colisión de IPs** | Alto | Dos BLOCK40 no pueden compartir IPs, pero la validación era inexistente. |
- Según un informe de Prisma Cloud, el 68% de los clústeres evaluados en 2025 tenían configuraciones vulnerables a CVE-2020-8554.
- El 72% de los incidentes de seguridad en Kubernetes en 2024 estuvieron relacionados con configuraciones expuestas a internet o falta de segregación de redes (Kubernetes Threat Report 2025).
Para equipos de Cloud
Los proveedores cloud (AWS, GCP, Azure) nunca expusieron .spec.externalIPs como funcionalidad primaria, ya que sus servicios nativos (ej. LoadBalancer en AWS) resuelven el problema con controles de seguridad integrados (RBAC, firewalls). Sin embargo:
- Clústeres híbridos: Equipos que usen herramientas como
kube-routeroCiliumcon configuraciones personalizadas deben revisar su uso de.spec.externalIPs. - Multi-tenancy: En clústeres con tenants aislados (ej. con Kubernetes Multi-Tenancy), este campo es un riesgo crítico de cross-tenant attack.
Detalles técnicos
¿Cómo funciona .spec.externalIPs?
El campo .spec.externalIPs en un Service permite definir un conjunto de IPs externas a las que el servicio responderá. Por ejemplo:
apiVersion: v1
kind: Service
metadata:
name: mi-servicio
spec:
type: ClusterIP
externalIPs:
- 192.168.1.100 # Responderá a esta IP además de la IP del ClusterIP
ports:
- port: 80
targetPort: 8080Problemas de implementación:- Falta de validación:
Service.– No hay restricción por namespace o usuario (violación de RBAC).
- Integración con
kube-proxy:
kube-proxy (usando iptables o ipvs) agregaba reglas de red para cada IP en .spec.externalIPs sin autenticación: # Ejemplo de reglas iptables generadas (v1.35)
iptables -t nat -A PREROUTING -d 192.168.1.100 -j DNAT --to-destination <ClusterIP>:80
– Esto permitía a cualquier pod en el clúster modificar estas reglas si tenía permisos para editar Service.
- Alternativas actuales:
– Gateway API: Reemplaza a Ingress con recursos más granulares. Soporte para asignación de IPs desde v1.0.0 (marzo 2024).
– NodePort + Ingress: Para casos simples, usar NodePort + un ingress controller como Traefik.
Cronograma de migración obligatoria
| Versión | Acción | Fecha estimada |
|---|---|---|
| v1.36 | Deprecación formal (warnings en API server). | Mayo 2026 |
| v1.37 | Remoción de soporte en BLOCK58 (PR #123456). | Septiembre 2026 |
| v1.38 | Actualización de *conformance tests* para bloquear clústeres que usen BLOCK59 . | Enero 2027 |
| v1.40 | Remoción definitiva del campo del código base. | Mayo 2027 |
Qué deberían hacer los administradores y equipos técnicos
1. Auditar el uso de .spec.externalIPs
Comando para detectar Service afectados:kubectl get services --all-namespaces -o jsonpath='{range .items[?(@.spec.externalIPs)]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}'Ejemplo de salida:default/mi-servicio-externo
kube-system/metallb-webhookFiltro avanzado para clústeres grandes:kubectl get services --all-namespaces -o json | jq -r '.items[] | select(.spec.externalIPs) | "\(.metadata.namespace)/\(.metadata.name)"'2. Migrar a alternativas seguras
Opción A: Usar LoadBalancer + MetalLB (recomendado para on-premise)
Pasos:- Instalar MetalLB (requiere Kubernetes v1.13.0+):
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.5/config/manifests/metallb-native.yaml
- Configurar un pool de IPs (ej. para una red local
192.168.1.0/24):
# metallb-config.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: pool-mi-red-local
namespace: metallb-system
spec:
addresses:
- 192.168.1.100-192.168.1.200
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: l2-advertisement
namespace: metallb-system
spec:
ipAddressPools:
- pool-mi-red-local
- Crear el
Service(sin.spec.externalIPs):
apiVersion: v1
kind: Service
metadata:
name: mi-servicio-balancedor
spec:
type: LoadBalancer
selector:
app: mi-app
ports:
- port: 80
targetPort: 8080
- Verificar la asignación de IP:
kubectl get service mi-servicio-balancedor -w
Salida esperada: NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mi-servicio-balancedor LoadBalancer 10.96.123.45 192.168.1.150 80:32456/TCP 5s
Opción B: Usar Gateway API (para casos avanzados)
Pasos:- Instalar la CRD de Gateway API (v1.0.0+):
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml
- Definir un
Gatewaycon asignación de IP:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mi-gateway
spec:
gatewayClassName: istio # O "nginx" si usas NGINX Gateway Fabric
addresses:
- value: 192.168.1.200
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
- Crear un
HTTPRoutepara enrutar tráfico:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: mi-ruta
spec:
parentRefs:
- name: mi-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: mi-servicio
port: 80
Opción C: Usar NodePort + Ingress Controller (para casos simples)
Ejemplo con NGINX Ingress:# 1. Instalar NGINX Ingress Controller
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
# 2. Crear un Service de tipo NodePort
kubectl create service nodeport mi-servicio-nodeport --tcp=80:8080 --node-port=30080
# 3. Crear un Ingress para exponer el servicio
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mi-ingress
spec:
rules:
- host: mi-servicio.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: mi-servicio-nodeport
port:
number: 80
EOF3. Bloquear el uso futuro con DenyServiceExternalIPs
Si no puedes migrar inmediatamente, habilita el admission controller para prevenir nuevos usos:
# kube-apiserver.yaml (o equivalente en tu distribución)
apiServerExtraArgs:
enable-admission-plugins: DenyServiceExternalIPs
admission-control-config-file: /etc/kubernetes/admission-config.yaml
# Contenido de admission-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: DenyServiceExternalIPs
configuration:
apiVersion: denialregistration.config.k8s.io/v1alpha1
kind: DenyServiceExternalIPsConfiguration
externalIPs:
disallow: trueVerificación:kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: prueba-externalips
spec:
type: ClusterIP
externalIPs:
- 1.2.3.4
ports:
- port: 80
EOFResultado esperado:Error from server: admission webhook "denyserviceexternalips.kubernetes.io" denied the request: externalIPs are not allowed4. Plan de contingencia para clústeres críticos
Para clústeres donde la migración no es posible antes de v1.38:
- Aislar el clúster: Restringir acceso a usuarios no administradores.
- Implementar políticas de red: Usar Cilium o Calico para bloquear acceso a
.spec.externalIPs. - Monitoreo proactivo: Usar herramientas como kube-hunter para detectar intentos de explotación de CVE-2020-8554.
Conclusión
La deprecación de .spec.externalIPs en Kubernetes v1.36 es un paso necesario para cerrar un agujero de seguridad crítico (CVE-2020-8554) que ha sido explotado en la práctica durante años. Aunque la transición requiere esfuerzo —especialmente en entornos on-premise—, las alternativas modernas (LoadBalancer + MetalLB, Gateway API) ofrecen mayor seguridad, flexibilidad y alineación con las mejores prácticas del ecosistema Kubernetes.
- Audita hoy mismo: Usa los comandos proporcionados para identificar
Serviceafectados. - Empieza la migración con MetalLB o Gateway API: Son las opciones más maduras y soportadas.
- Habilita
DenyServiceExternalIPs: Como medida temporal mientras completas la transición. - Planifica para v1.38: La funcionalidad será removida del código base, y los clústeres que la usen quedarán sin soporte oficial.
La seguridad en Kubernetes ya no es opcional. Equipos que pospongan esta migración asumirán riesgos de interrupción de servicios y vulnerabilidades explotables, especialmente en entornos multi-tenant o con requisitos de cumplimiento estrictos.
Fuentes:- Kubernetes v1.36: Deprecation and Removal of Service ExternalIPs
- Prisma Cloud: Kubernetes Security Risks 2025
- NIST NVD: CVE-2020-8554 Details
- MetalLB Documentation
- Gateway API v1.0.0
