Introducción
La divergencia entre la infraestructura definida como código y el estado real del clúster es una fuente recurrente de vulnerabilidades silenciosas en entornos Kubernetes gestionados. Equipos de DevOps y seguridad a menudo confían en que los archivos de configuración en repositorios reflejan exactamente qué servicios están expuestos a Internet, pero esto falla cuando existen controllers de admisión dinámicos, anotaciones de proxy mal aplicadas o recursos creados por herramientas externas al flujo CI/CD tradicional. Shopify enfrentó este problema con su scanner interno inicial, el cual generaba falsos positivos y puntos ciegos significativos al analizar Ingresses en Google Kubernetes Engine (GKE) y servicios de Cloud Run.
La solución adoptada, denominada Overexposed, cambia el paradigma de la validación estática a la verificación dinámica basada en inventario. En lugar de depender exclusivamente del repositorio Git, la herramienta consulta la API de Cloud Asset Inventory de Google para obtener el estado aceptado por el cluster y, posteriormente, realiza una sonda HTTP anónima contra cada endpoint candidato. Este enfoque híbrido permite detectar exposición real sin credenciales, mapeando seis veredictos precisos que diferencian entre un servicio protegido, uno expuesto intencionalmente y uno simplemente no encontrado o inaccesible, proporcionando una imagen fiel de la superficie de ataque externa.
Qué ocurrió
El escáner original de Shopify operaba bajo una lógica sencilla pero frágil: revisaba los archivos de configuración en el repositorio de infraestructura para identificar hostnames públicos sin configuraciones de autenticación explícitas y luego intentaba sondearlos. Este método falló en tres escenarios críticos que comprometían la integridad del análisis. Primero, equipos que utilizaban anotaciones de autenticación de proxy directamente disparaban falsos positivos porque el scanner no interpretaba correctamente la lógica de negocio detrás de esas configuraciones. Segundo, los controllers de admisión que reescriben la configuración en tiempo real (admission time) pero que no se ejecutan consistentemente en todos los clusters provocaban que una configuración aparentemente correcta en el repositorio no tuviera efecto real en producción. Tercero, los Ingresses creados por otras herramientas de plataforma o repositorios secundarios nunca eran detectados, creando puntos ciegos totales para la visibilidad de seguridad.
Para remediar esto, el equipo de ingeniería reconstruyó Overexposed basándose en dos pilares técnicos: Cloud Asset Inventory y sondas HTTP anónimas. La nueva versión consulta el inventario de activos de GCP, que incluye todos los Ingresses aceptados por GKE después del proceso de admisión, además de servicios de Cloud Run y balanceadores de carga. Un segundo paso recupera las políticas de acceso sin necesidad de credenciales de cluster, lo que simplifica la arquitectura de permisos. Durante las primeras semanas de ajuste, esta metodología redujo los veredictos inconclusos en aproximadamente un 80%. Lo crucial aquí es el principio rector adoptado por Shopify: el repositorio muestra la intención del desarrollador, pero el clúster (y su inventario) muestra lo que realmente se impone y ejecuta.
Impacto para DevOps / Infraestructura / Cloud / Seguridad
Para los equipos de infraestructura en GCP, este enfoque tiene implicaciones directas sobre cómo se gestionan las políticas IAM y la visibilidad de red. La distinción entre «exposición por configuración de Ingress» y «exposición por roles IAM» es fundamental en Cloud Run. Un servicio puede parecer protegido por su configuración de Ingress, pero si el rol allUsers tiene permiso de invocador o si la verificación de invocador está deshabilitada, el servicio es público. Overexposed distingue estas capas, evitando que los administradores asuman protección donde solo hay una barrera débil o inexistente debido a permisos granulares mal asignados.
Desde la perspectiva de seguridad, la reducción del ruido operativo es tangible. En una ejecución temprana en julio, tres de cada cuatro resultados eran inconclusos, lo que paralizaba la capacidad de respuesta ante incidentes de exposición accidental. Al clasificar los resultados en seis categorías (exposed, expected, protected, not found, unreachable, inconclusive), el equipo de seguridad puede automatizar alertas precisas. Por ejemplo, un endpoint marcado como expected que aparece en una lista blanca revisada en código sigue siendo sondeado en cada ejecución; si cambia a protected o desaparece, se marca como obsoleto (stale). Esto previene la acumulación de reglas de firewall o listas blancas que ya no tienen sentido operativo pero que permanecen activas por inercia.
Detalles técnicos
La implementación técnica de Overexposed prioriza la robustez frente a las ambigüedades del protocolo HTTP y la infraestructura subyacente. Cada candidato identificado en el inventario recibe una sola solicitud anónima. El scanner utiliza primero un método HEAD para minimizar la carga, cayendo a GET si es necesario, y sigue hasta tres redirecciones, verificando cada salto. Se prueban también variaciones de ruta sensibles a mayúsculas/minúsculas, como /ADMIN, ya que las reglas de ruta en Ingress pueden ser estrictas.
El manejo de códigos de estado HTTP requiere contexto específico por plataforma, un detalle que suele pasarse por alto en escáneres genéricos. Un código 404 detrás de un Ingress de GKE indica que el proxy respondió y la ruta está ausente (el servicio existe pero no ese endpoint). Sin embargo, en Cloud Run, el frontend de Google puede devolver un 404 antes de que la aplicación se ejecute si no hay una configuración correcta, por lo que el scanner clasifica esto como inconclusive para evitar falsos negativos. De manera similar, un 403 Forbidden no es prueba automática de protección; en Cloud Run, debe correlacionarse con los ajustes de IAM y la presencia de Identity-Aware Proxy (IAP) en el mapa de URL del balanceador de carga.
La seguridad interna del propio scanner fue reforzada contra Server-Side Request Forgery (SSRF). Dado que Overexposed realiza peticiones salientes a hostnames arbitrarios, valida cada IP devuelta por DNS, rechaza direcciones privadas y de loopback, y se conecta directamente a la IP verificada para prevenir ataques de rebinding de DNS. Además, el equipo utiliza pruebas de mutación (mutation testing) en los guardias de seguridad del código Python/Rust que componen la lógica, asegurando que las pruebas no pasen por accidente mientras verifican la lógica incorrecta.
Qué deberían hacer los administradores y equipos técnicos
Equipos que gestionan GKE o Cloud Run deben evaluar si su actual estrategia de descubrimiento de exposición depende únicamente de archivos de configuración (IaC) o si incorporan una verificación dinámica del estado real. Si usan herramientas como Terraform o Pulumi, es recomendable implementar un job programado que consulte gcloud asset list para obtener Ingresses y servicios Cloud Run activos, filtrando aquellos sin políticas de autenticación explícitas.
Para replicar la lógica de sonda anónima, se puede crear un script simple en Python que itere sobre los endpoints descubiertos. A continuación, se muestra un ejemplo conceptual de cómo estructurar la verificación de exposición básica considerando el contexto de Cloud Run:
import requests
def check_exposure(url):
# Intentar HEAD primero para eficiencia
try:
response = requests.head(url, allow_redirects=True, timeout=5)
if response.status_code == 403:
# En Cloud Run, verificar IAM manualmente si es posible vía API
return "Potential Exposure (Check IAM allUsers role)"
elif response.status_code in [200, 201, 202, 204]:
return "Exposed"
elif response.status_code == 404:
# Contexto necesario: ¿Es GKE o Cloud Run?
return "Inconclusive (Check backend availability)"
except requests.exceptions.RequestException as e:
return f"Unreachable: {str(e)}"
# Ejemplo de uso con endpoint Cloud Run público
print(check_exposure("https://my-service-xyz.run.app"))
Los administradores deben auditar las listas blancas (allowlists) existentes. No basta con marcar un endpoint como «público intencional»; se debe verificar periódicamente que la lógica de negocio detrás de ese endpoint no haya cambiado hacia una necesidad de autenticación. Asimismo, asegúrense de que sus herramientas de seguridad restrinjan el egreso de red del escáner para evitar accesos a metadatos de la nube (cloud metadata endpoints) durante las pruebas SSRF.
Conclusión
La transición de Shopify de un escáner basado en repositorios a uno basado en inventario y sondas activas ilustra una lección clave para la seguridad en cloud-native: la configuración declarativa es insuficiente sin una validación operativa continua. Al reducir los falsos positivos y los puntos ciegos causados por controllers de admisión y recursos externos, Overexposed permite a los equipos de DevOps confiar en que lo que ven en el dashboard es lo que realmente expone su infraestructura a Internet. Para cualquier organización que use GKE o Cloud Run, implementar una capa de verificación anónima sobre el inventario de activos cierra la brecha entre la intención del desarrollador y la realidad del entorno productivo, mitigando riesgos de exposición accidental de forma automatizada y precisa.
Fuentes
https://www.infoq.com/news/2026/11/shopify-kubernetes-ingresses/?utm_campaign=infoq_content&utm_source=infoq&utm_medium=feed&utm_term=global