Gestión de secrets con Vault, Azure Key Vault y rotación

Avanzado
Spring Security
Spring Security
Actualizado: 19/04/2026

Mantener secrets (contraseñas de BD, claves de API, certificados) en application.yaml o variables de entorno es uno de los riesgos más comunes en producción. Una sola fuga del repositorio o de un dump de configuración expone toda la infraestructura.

Las soluciones modernas centralizan los secrets en un secret manager y los inyectan en la aplicación bajo demanda. Las dos opciones dominantes en el ecosistema Java son HashiCorp Vault (autoalojado o Vault Cloud) y Azure Key Vault (gestionado en la nube de Microsoft).

Anti-patrones que debes evitar

Antes de ver las soluciones, repasamos lo que no se debe hacer.

  • 1. Commit de application-prod.yaml: incluso en repos privados, cualquier desarrollador puede leerlos. Audita cada cambio.
  • 2. Variables de entorno con valores en claro: visibles en ps, docker inspect, dashboards de Kubernetes. Son aceptables como referencia a un secret, no como contenedor del valor.
  • 3. Hardcoding en el código: aparece en backups, en historiales de Git aunque se borre, en logs de stack trace.
  • 4. Secrets compartidos entre entornos: si filtras la clave de staging y es la misma que producción, ya tienes el incidente.

La regla de oro: en producción, ningún secret debe poder leerse desde un dump de la configuración o desde el código compilado. Solo desde el secret manager autenticado.

Arquitectura con HashiCorp Vault

flowchart LR
    A[Spring Boot] -->|AppRole login| B[Vault]
    B -->|Token con TTL| A
    A -->|Read secret/data/db| B
    B -->|JSON con username y password| A
    A -->|Conexion JDBC| C[(PostgreSQL)]
    D[Operador] -->|Rotar credencial| B
    B -->|Notify| A

Los componentes son:

  • Vault Server: cluster en HA con backend de almacenamiento (Consul, Raft).
  • Engine kv-v2: almacena secrets estáticos versionados.
  • Engine database: genera credenciales dinámicas con TTL corto.
  • AppRole: método de autenticación pensado para servicios (RoleID + SecretID).

Spring Cloud Vault: configuración mínima

Añadimos la dependencia a Spring Cloud Vault.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>

Y configuramos el application.yaml. Los datos de conexión a Vault vienen de variables de entorno, mientras que los secrets se proyectan como properties.

spring:
  application:
    name: pagos-service
  cloud:
    vault:
      uri: ${VAULT_ADDR:https://vault.demo.local:8200}
      authentication: APPROLE
      app-role:
        role-id: ${VAULT_ROLE_ID}
        secret-id: ${VAULT_SECRET_ID}
        role: pagos
      kv:
        enabled: true
        backend: secret
        default-context: pagos-service
        application-name: pagos-service

Si en Vault hay un secret en secret/data/pagos-service con las claves db.username y db.password, Spring las expone como properties habituales.

spring:
  datasource:
    url: jdbc:postgresql://db.demo.local:5432/pagos
    username: ${db.username}
    password: ${db.password}

En Kubernetes, Vault Agent Injector monta los secrets como volúmenes y elimina la dependencia de spring-cloud-vault. Es la opción preferida cuando el operador de Vault está en el mismo cluster.

Credenciales dinámicas de base de datos

El motor database de Vault genera usuarios efímeros de PostgreSQL/MySQL con TTL configurable. Cada microservicio recibe un usuario único, lo que mejora drásticamente la auditoría.

Configuración en Vault (CLI):

vault secrets enable database

vault write database/config/pagos \
    plugin_name=postgresql-database-plugin \
    allowed_roles="pagos-rw" \
    connection_url="postgresql://{{username}}:{{password}}@db.demo.local:5432/pagos?sslmode=disable" \
    username="vault" \
    password="rootpwd"

vault write database/roles/pagos-rw \
    db_name=pagos \
    creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
    default_ttl="1h" \
    max_ttl="24h"

Y en Spring:

spring:
  cloud:
    vault:
      database:
        enabled: true
        role: pagos-rw
        backend: database

Spring rota la credencial automáticamente y notifica a HikariCP cuando expira. La aplicación no ve el cambio.

Rotación de secrets sin downtime

La rotación es el verdadero motivo para usar un secret manager. Cuando un operador cambia la clave de un servicio externo, queremos que la aplicación la recoja sin reiniciarse.

Spring Cloud Vault expone el endpoint /actuator/refresh (necesita el starter spring-cloud-starter-bootstrap y spring-boot-starter-actuator). Una llamada POST reconstruye los beans anotados con @RefreshScope.

@Component
@RefreshScope
public class TwilioClient {

    private final String apiKey;

    public TwilioClient(@Value("${twilio.api-key}") String apiKey) {
        this.apiKey = apiKey;
    }
}

Tras rotar el secret en Vault, basta con curl -X POST http://app:8080/actuator/refresh para que el bean se reconstruya con la nueva clave.

Para flotas grandes, Spring Cloud Bus propaga el evento RefreshEvent por RabbitMQ o Kafka, y todas las instancias se actualizan a la vez sin scripts manuales.

Azure Key Vault con Spring Cloud Azure

En Azure, la integración nativa es Azure Key Vault Secrets. Los secrets se referencian con la URL https://mi-vault.vault.azure.net/secrets/{nombre}.

<dependency>
    <groupId>com.azure.spring</groupId>
    <artifactId>spring-cloud-azure-starter-keyvault-secrets</artifactId>
</dependency>
spring:
  cloud:
    azure:
      keyvault:
        secret:
          property-sources:
            - endpoint: "https://mi-vault.vault.azure.net"

Las identidades Managed Identity del recurso (App Service, AKS, VM) se autentican contra Key Vault sin guardar credenciales. La rotación se programa con Event Grid y la app la recoge en el siguiente ciclo de actualización.

Compara los SLA. Vault on-premise depende de tu equipo; Azure Key Vault tiene 99.99 % de disponibilidad pero también un coste por operación que puede sumar en sistemas con muchos lookups.

Cifrado de propiedades sensibles en disco

Cuando no es viable un secret manager (proyectos pequeños, ambientes air-gapped), una alternativa es cifrar las propiedades sensibles con Jasypt.

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>
spring:
  datasource:
    password: ENC(K3yhE8W4j...)

La password de cifrado se inyecta por variable de entorno o por argumento de línea de comandos.

java -Djasypt.encryptor.password=secret -jar app.jar

Esto no es equivalente a Vault, pero protege frente a la lectura accidental del repositorio.

Auditoría de accesos al secret manager

Todo secret manager registra cada acceso. En una incidencia de seguridad, esos logs son la primera fuente de información para saber qué se ha leído y desde dónde.

  • Vault: configurar audit enable file o audit enable syslog. Los logs incluyen el path del secret, el cliente y el timestamp.
  • Azure Key Vault: activar Diagnostic Settings y enviarlos a Log Analytics.
  • AWS Secrets Manager: integrarlo con CloudTrail.

Bloquea el acceso por políticas estrictas. El microservicio de pagos solo debería poder leer secret/data/pagos/*, no secret/data/usuarios/*. Una política bien definida limita el daño de un compromiso.

Errores frecuentes

  • Cachear el secret indefinidamente: si la app guarda la API key en un campo static final, la rotación no surte efecto. Usa @RefreshScope o lee el secret en cada llamada (con cache de pocos segundos).
  • Loguear secrets: nunca loguees el resultado de secret.getData(). Configura un filtro Logback que detecte y enmascare patrones tipo password=....
  • Compartir el AppRole entre microservicios: cada servicio debe tener su propio rol. Comprometer uno no debe dar acceso a los demás.
  • Olvidar el plan de fallback: si Vault está caído, ¿la app arranca? Configura spring.cloud.vault.fail-fast=true para fallar rápido y delega en Kubernetes el reintento.
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Spring Security es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Spring Security

Explora más contenido relacionado con Spring Security y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Integrar Spring Boot con HashiCorp Vault y Azure Key Vault para leer secrets en arranque, refrescarlos en caliente con Spring Cloud Bus y rotar credenciales de base de datos.