Spring Boot Actuator: qué exponer, qué ocultar y qué mirar antes de agregar endpoints
Hay una configuración que aparece en casi todos los tutoriales de Spring Boot y que se copia sin leerla bien:
management:
endpoints:
web:
exposure:
include: "*"
Un asterisco. Todos los endpoints. De una. Y el tutorial sigue con el paso siguiente sin detenerse un segundo en lo que eso significa.
Mi tesis es simple: Actuator no es malo. El error es habilitarlo sin una política clara de qué exponer, qué restringir y para quién. Esa diferencia entre herramienta operativa y superficie pública innecesaria no la resuelve Spring Boot solo: la resolvés vos con criterio explícito.
Tengo más de treinta años de historia con la tecnología, y uno de los patrones que más veo repetirse no es el bug dramático que tira producción: es la decisión pequeña tomada sin política, que se acumula hasta que alguien la encuentra desde afuera.
Spring Boot Actuator endpoints seguridad: qué dice la documentación y qué no dice
La documentación oficial de Spring Boot Actuator es clara en algo que los tutoriales suelen saltear: por defecto, sobre HTTP solo están habilitados health e info. El resto existe pero no se expone automáticamente.
Lo que la documentación describe con precisión:
- La distinción entre habilitar un endpoint (que exista en el contexto) y exponerlo (que sea accesible por HTTP o JMX).
- Que
shutdownestá habilitado pero no expuesto por defecto, precisamente porque su impacto es irreversible. - Que
envyconfigpropspueden mostrar valores sensibles, y que Spring Boot ofrece sanitización configurable para esos casos. - Que la seguridad de los endpoints es responsabilidad del desarrollador: Spring Boot no agrega autenticación automática sobre Actuator.
Lo que la documentación no resuelve por vos:
- Qué endpoints tienen sentido para vos operación concreta.
- Si el puerto de management está separado del puerto de la app o compartido.
- Si hay un proxy, API gateway o balanceador de carga delante que filtra o no esas rutas.
- Si el equipo que opera el sistema tiene acceso diferenciado del tráfico público.
Esa brecha entre "lo que Spring Boot configura por defecto" y "lo que necesitás en producción" es exactamente donde se acumulan las decisiones implícitas.
El error más común: habilitar todo y securizar después (o nunca)
La receta que circula en tutoriales, Stack Overflow y repositorios de ejemplo suele tener esta forma:
# application.yml — patrón común que vale revisar antes de copiar
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
El costo oculto de este patrón no es que "sea peligroso" en abstracto. El costo concreto es que:
-
/actuator/envexpone variables de entorno, incluyendo claves, URLs de base de datos y tokens si no están sanitizadas. -
/actuator/heapdumppuede devolver un dump del heap de la JVM, que en muchos sistemas contiene datos en memoria. -
/actuator/shutdownpermite apagar la aplicación vía HTTP POST si está habilitado y expuesto. -
/actuator/loggerspermite cambiar el nivel de logging en caliente, lo que puede generar ruido operativo o exponer información de diagnóstico.
Ninguno de esos endpoints es malo en sí mismo. heapdump es invaluable para diagnosticar un leak de memoria. loggers es exactamente la herramienta que querés cuando necesitás DEBUG sin reiniciar. El problema es exponerlos sin decidir quién puede acceder y desde dónde.
La comparación que me resulta útil: es parecido a lo que pasa con los Docker healthchecks. Un healthcheck mal diseñado no falla porque Docker sea malo; falla porque nadie definió qué significa "sano" para ese contenedor en particular. Con Actuator pasa lo mismo: la herramienta es sólida, pero el criterio de exposición tiene que ser explícito.
Matriz de decisión: qué exponer, qué restringir, qué directamente no habilitar
Esta matriz es un criterio prudente, no una regla absoluta. Cada fila representa un análisis que vale hacer antes de incluir ese endpoint.
| Endpoint | ¿Qué expone? | Criterio recomendado |
|---|---|---|
health |
Estado de la app y sus dependencias | Exponer. show-details: when-authorized en producción |
info |
Metadata de build, versión, git commit | Exponer si hay datos útiles. No expone nada sensible por defecto |
metrics |
Métricas JVM, HTTP, custom | Exponer solo a sistemas de monitoreo internos (Prometheus, etc.) |
loggers |
Niveles de logging actuales | Restringir a operadores. Útil, pero con acceso controlado |
env |
Variables de entorno | Restringir. Revisar sanitización antes de habilitar |
configprops |
Propiedades de configuración | Restringir. Puede mostrar valores sensibles |
heapdump |
Dump del heap de la JVM | Solo en ambientes controlados. No en producción pública |
threaddump |
Estado de los threads activos | Útil para diagnóstico. Restringir a operadores |
shutdown |
Apaga la aplicación | No habilitar salvo que haya una razón operativa muy clara |
mappings |
Todos los endpoints REST de la app | Información de superficie. Restringir |
beans |
Todos los beans del contexto Spring | Solo en desarrollo. No en producción |
La configuración que yo usaría como punto de partida en un ambiente productivo:
# application.yml — punto de partida prudente para producción
management:
server:
port: 8081 # puerto separado, no expuesto públicamente
endpoints:
web:
exposure:
include: health, info, metrics, loggers
endpoint:
health:
show-details: when-authorized
env:
enabled: false # deshabilitar si no hay política de sanitización definida
Separar el puerto de management (management.server.port) del puerto de la aplicación es una decisión que simplifica mucho la política: el tráfico interno llega al 8081, el tráfico público no tiene acceso a ese puerto en absoluto. Es una forma de convertir la seguridad en una propiedad estructural en lugar de depender solo de autenticación.
Lo que no podés concluir sin experimento, logs o datos propios
Hay un límite honesto que vale nombrar. Esta guía está basada en la documentación oficial y en análisis prudente. Lo que no podés concluir solo con esto:
- Que vos configuración actual es segura: eso requiere revisar qué hay delante de la aplicación (proxy, gateway, firewall) y cómo está configurado.
- Que separar el puerto de management es suficiente: si el puerto 8081 está expuesto en el security group o en el firewall de red, la separación no sirve.
- Que la sanitización de
envcubre todas las claves sensibles: Spring Boot sanitiza por patrones conocidos (password,secret,key, etc.), pero si usás nombres de variables no convencionales, vale verificarlo. - Que agregar Spring Security sobre Actuator es la única solución: también podés resolver el acceso a nivel de red, de proxy o de ingress. La capa correcta depende de la arquitectura.
Si querés inspeccionar qué tráfico llega realmente a los endpoints de management en un ambiente local, una herramienta como Sniffnet puede ayudarte a visualizar conexiones sin meterte en tcpdump desde el primer momento.
FAQ: Spring Boot Actuator endpoints seguridad
¿Cuáles son los endpoints de Actuator que están habilitados por defecto?
Según la documentación oficial, sobre HTTP solo health e info están expuestos por defecto. El resto están habilitados en el contexto pero no accesibles por HTTP. La excepción es shutdown, que no está habilitado por defecto justamente por su impacto.
¿Qué diferencia hay entre habilitar y exponer un endpoint?
Habilitar significa que el endpoint existe en el contexto de Spring y puede ser invocado internamente. Exponer significa que es accesible desde el exterior (HTTP o JMX). Podés tener un endpoint habilitado y no expuesto: existe pero nadie puede llamarlo desde fuera. La propiedad management.endpoints.web.exposure.include controla la exposición HTTP.
¿Es suficiente con Spring Security para proteger Actuator?
Spring Security es una capa válida, pero no es la única ni necesariamente la primera línea. Separar el puerto de management y restringir acceso a ese puerto a nivel de red o proxy es una política más robusta porque no depende de la correcta configuración de cada regla de seguridad en el código. Idealmente, ambas capas conviven.
¿/actuator/health expone información sensible?
Con la configuración por defecto, health devuelve solo el estado (UP o DOWN). El detalle de dependencias (base de datos, disco, etc.) aparece solo si configurás show-details: always. La opción intermedia y más razonable en producción es show-details: when-authorized: el sistema de monitoreo ve el detalle, el tráfico anónimo no.
¿Qué pasa si uso include: "*" en producción?
Se exponen todos los endpoints habilitados, incluyendo env, configprops, heapdump y potencialmente shutdown si lo habilitaste. No es una configuración inválida en todos los contextos, pero requiere que el acceso al puerto de management esté completamente restringido a sistemas internos. Si ese puerto es accesible públicamente, cualquiera puede consultar variables de entorno o pedir un dump del heap.
¿Cómo valido qué endpoints están realmente expuestos en mi app?
El endpoint /actuator devuelve un JSON con los endpoints activos y sus URLs. Es el primer lugar donde mirar. También podés revisar los logs de startup de Spring Boot: loguea los endpoints registrados si tenés el nivel adecuado. Y si tenés Prometheus o cualquier sistema de scraping configurado, verificá que solo raspe el puerto de management y no el puerto público.
Mi postura: Actuator es una herramienta operativa, no una opción de conveniencia
Cuando vi por primera vez la anécdota de Docker que mencioné al principio del post sobre healthchecks, lo que me quedó fue la misma lección que aplica acá: el valor de la herramienta está en describir un contrato explícito, no en activarla y ver qué pasa.
Con Actuator es igual. No es una feature que "agregás" a la app. Es una política de observabilidad que define qué información operativa se expone, a quién y desde dónde. Esa política puede ser tan simple como dos líneas en el YAML o tan elaborada como roles diferenciados con Spring Security, pero tiene que existir de forma consciente.
Lo incómodo es que el tutorial que más circula muestra el include: "*" como si fuera el paso natural. Y técnicamente funciona. Pero "funciona" y "es una decisión tomada con criterio" son cosas distintas, y la diferencia importa cuando el sistema crece.
La decisión práctica para el próximo paso es esta: abrí el application.yml del proyecto en el que estás trabajando ahora, buscá la sección management.endpoints, y preguntate si lo que está ahí es una política explícita o el resultado de copiar un tutorial sin cuestionarlo. Si es lo segundo, dedicale quince minutos antes de que el sistema salga a un ambiente compartido.
No porque Actuator sea peligroso. Sino porque las decisiones implícitas son las que se acumulan.
Fuentes:
Este artículo fue publicado originalmente en juanchi.dev
Top comments (0)