Eran las 4:00 PM de un viernes (el momento clásico para los desastres) cuando el panel de monitoreo de Prometheus se iluminó en rojo. Nuestro clúster de producción, ejecutando Kubernetes 1.29 con una arquitectura de microservicios sobre Istio 1.20, comenzó a arrojar una tasa de error del 15% en las comunicaciones servicio-a-servicio. Los logs de los istio-proxy (Envoy) no mostraban un bloqueo de red tradicional, sino el temido mensaje: upstream connect error or disconnect/reset before headers. reset reason: connection termination.
A primera vista, parecía un problema de recursos en los pods, pero tras escalar los HPA, el error persistía. Estábamos ante un fallo en la capa de Seguridad K8s: la autenticación mutua (mTLS) había fallado silenciosamente, dejando a los servicios incapaces de confiar unos en otros. En este artículo, detallaré cómo diagnosticamos este incidente de Istio mTLS usando herramientas de bajo nivel y cómo implementamos una solución definitiva para la Gestión de certificados utilizando cert-manager.
Análisis del Incidente: Cuando los Secretos Caducan
El entorno afectado manejaba cerca de 15,000 RPS entre microservicios de Go y Node.js. Istio estaba configurado en modo STRICT mTLS globalmente. Esto significa que ningún tráfico en texto plano está permitido dentro de la malla. La Depuración Istio en estos casos es compleja porque el tráfico cifrado es opaco para herramientas como tcpdump a menos que tengas las claves.
[2024-05-12T10:00:00.000Z] "POST /api/v1/checkout HTTP/1.1" 503 UC upstream_reset_before_response_started{connection_termination} - "unknown_error"
Inicialmente, asumimos que era una desconfiguración en un DestinationRule. Verifiqué si alguien había desplegado una regla específica que deshabilitara mTLS para un servicio concreto, rompiendo la negociación con el modo estricto del servidor. Sin embargo, la configuración YAML estaba inmaculada.
La causa raíz residía en la identidad de la carga de trabajo (Workload Identity). Los certificados X.509 que Istiod distribuye a los sidecars tienen una vida útil corta (por defecto 24 horas). Si el mecanismo de rotación falla, o si el certificado raíz (CA) del que dependen estos certificados caduca, toda la confianza se rompe instantáneamente.
El error del "Reinicio Rápido"
Mi primer instinto fue reiniciar los pods afectados (kubectl rollout restart). Esto forzó a los nuevos pods a solicitar nuevos certificados a Istiod. Funcionó... durante 10 minutos. Luego, otros servicios empezaron a fallar. Al reiniciar ciegamente, no solucioné el problema subyacente: la CA raíz personalizada que habíamos inyectado manualmente hace un año estaba expirando. Al intentar solucionar el Troubleshooting Service Mesh de forma reactiva, ignoramos la cadena de confianza completa. Si estás luchando con conceptos similares, revisa la documentación oficial de Istio Security.
Solución Fase 1: Inspección Profunda con istioctl
Para confirmar la hipótesis de certificados caducados o no sincronizados, no puedes confiar solo en los logs. Debes interrogar al proxy Envoy directamente. La herramienta istioctl es vital aquí.
El siguiente comando permite inspeccionar los secretos cargados en la memoria del sidecar de un pod específico. Esto verifica si el plano de control (Istiod) ha enviado correctamente las claves.
// Verificamos el estado de los certificados en el proxy
// Buscamos el estado 'Active' y la fecha de expiración
$ istioctl proxy-config secret deployment/checkout-service -n ecom-prod
RESOURCE NAME TYPE STATUS VALID CERT SERIAL NUMBER NOT AFTER
default Cert Chain ACTIVE true 129384912938... 2024-05-13T10:00:00Z
ROOTCA CA ACTIVE true 558120394812... 2024-05-12T09:59:00Z <!-- ¡EXPIRADO!
Aquí estaba la prueba humeante. El ROOTCA tenía una fecha NOT AFTER en el pasado. Aunque el certificado del servicio (default) era válido, la cadena de confianza fallaba porque la raíz había caducado. Ninguna cantidad de reinicios de pods arreglaría esto permanentemente si no rotábamos la autoridad certificadora raíz.
Solución Fase 2: Automatización con cert-manager
La gestión manual de certificados CA en Istio (creando el secreto cacerts manualmente) es un riesgo operativo inaceptable. Para resolver esto de forma definitiva, integramos cert-manager para controlar el ciclo de vida del certificado raíz de Istio.
La arquitectura es la siguiente: cert-manager actúa como el emisor (usando un ClusterIssuer que puede ser Vault, Let's Encrypt o un SelfSigned interno) y escribe el secreto cacerts en el namespace istio-system. Istiod observa este secreto y, al detectar cambios, recarga la CA y comienza a firmar nuevas cargas de trabajo automáticamente.
cacerts y contenga claves específicas: ca-cert.pem, ca-key.pem, root-cert.pem y cert-chain.pem.
A continuación se muestra la configuración para crear un emisor autofirmado (para pruebas o interna) y el certificado que Istio consumirá:
# 1. Crear un Issuer autofirmado (o conectar con Vault)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
---
# 2. El Certificado que rotará automáticamente
# cert-manager guardará esto en el secreto 'cacerts'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: istio-ca
namespace: istio-system
spec:
isCA: true
commonName: istio-system
secretName: cacerts # Istiod busca ESTE nombre exacto
subject:
organizations:
- "Mi Empresa Tech"
dnsNames:
- "istiod.istio-system.svc"
duration: 2160h # 90 días
renewBefore: 360h # Renovar 15 días antes
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
group: cert-manager.io
# Mapeo de claves para compatibilidad con Istio
keystores:
pkcs12:
create: false
jks:
create: false
Al aplicar este manifiesto, cert-manager asume el control. Cuando llega el periodo renewBefore, genera nuevas claves, actualiza el secreto cacerts, y Istiod propaga la nueva raíz a todos los sidecars automáticamente. Esto elimina el error humano y garantiza que la Seguridad K8s se mantenga intacta sin intervención manual.
| Métrica | Gestión Manual (Legacy) | Con cert-manager |
|---|---|---|
| Riesgo de Expiración | Alto (Depende de alertas/calendario) | Nulo (Automático) |
| Tiempo de Recuperación | 2-4 horas (Diagnóstico + Fix) | 0 (Rotación proactiva) |
| Validación de Cadena | Manual (openssl) | Automática (CRD Status) |
La tabla superior refleja la mejora operativa. Pasamos de tener incidentes críticos anuales a un proceso invisible. La clave no es solo la automatización, sino la observabilidad que nos da el recurso Certificate de Kubernetes, cuyo estado podemos monitorear fácilmente.
Efectos Secundarios y Advertencias
Implementar esta solución no está exento de riesgos. Un "Edge Case" crítico ocurre durante la rotación del certificado raíz. Aunque Istio soporta la rotación de CA sin tiempo de inactividad (enviando tanto el certificado raíz antiguo como el nuevo durante un periodo de transición), hemos observado problemas en versiones anteriores a la 1.18.
cacerts cambia, Istiod debe enviar actualizaciones XDS a todos los proxies conectados. En un clúster con 5,000 pods, esto puede causar un pico de CPU masivo en el plano de control. Asegúrese de que sus réplicas de Istiod tengan suficiente margen de CPU (al menos 2 vCPU) para manejar esta tormenta de actualizaciones.
Además, si utiliza un Issuer externo como Let's Encrypt para la CA de la malla (no recomendado, pero posible), tenga en cuenta los límites de tasa (rate limits). Para mTLS interno, es preferible usar una CA privada interna o Vault.
Conclusión
La gestión de Istio mTLS no termina al habilitar el modo estricto. La verdadera robustez proviene de una estrategia sólida de Gestión de certificados. Ignorar la rotación de la CA raíz es una bomba de tiempo en cualquier Service Mesh. Al combinar la capacidad de introspección de istioctl con la automatización de cert-manager, transformamos un sistema frágil en una arquitectura de seguridad resiliente y autogestionada.
Post a Comment