HashiCorp Vault
HashiCorp Vault es una solución de gestión de secretos diseñada para centralizar, proteger y controlar el acceso a información sensible como credenciales de bases de datos, claves API, certificados y tokens. A diferencia del Config Server que vimos en lecciones anteriores, Vault se especializa específicamente en gestionar secretos con cifrado automático, auditoría completa y controles de acceso granulares.
En entornos de microservicios, donde múltiples servicios necesitan acceder a secretos de manera segura, Vault proporciona una capa de abstracción que elimina la necesidad de hardcodear credenciales en código o archivos de configuración.
Arquitectura KV y conceptos básicos
Vault organiza los secretos mediante motores de secretos (secrets engines). El motor KV (Key-Value) es el más utilizado para almacenar secretos estáticos como credenciales y configuraciones sensibles.
El motor KV tiene dos versiones:
- KV v1: Almacenamiento simple sin versionado
- KV v2: Incluye versionado, metadatos y capacidades de eliminación suave
Para nuestro entorno de desarrollo utilizaremos KV v2, que viene habilitado por defecto en modo desarrollo. Los secretos se almacenan en rutas jerárquicas como secret/aplicacion/entorno/credencial
.
Configuración de Vault con Docker
Para configurar un entorno de desarrollo, utilizaremos Vault en modo dev con Docker. Este modo simplifica la configuración inicial pero no debe usarse en producción.
Creamos un archivo docker-compose.yml
para levantar Vault:
version: '3.8'
services:
vault:
image: hashicorp/vault:latest
container_name: dev-vault
restart: unless-stopped
ports:
- "8200:8200"
environment:
VAULT_DEV_ROOT_TOKEN_ID: 'dev-root-token'
VAULT_DEV_LISTEN_ADDRESS: '0.0.0.0:8200'
cap_add:
- IPC_LOCK
command: 'vault server -dev -dev-root-token-id=dev-root-token'
Iniciamos el contenedor:
docker-compose up -d
El token raíz dev-root-token
nos permite acceso completo durante el desarrollo. En el log del contenedor veremos la confirmación de que Vault está funcionando:
docker logs dev-vault
Almacenamiento de secretos con KV v2
Una vez que Vault está ejecutándose, podemos almacenar secretos utilizando la CLI. Primero, configuramos las variables de entorno:
export VAULT_ADDR='http://localhost:8200'
export VAULT_TOKEN='dev-root-token'
Almacenamos un secreto de base de datos en la ruta secret/microservicio/database
:
vault kv put secret/microservicio/database \
username=app_user \
password=secure_password_123 \
url=jdbc:postgresql://localhost:5432/appdb
Podemos verificar que el secreto se almacenó correctamente:
vault kv get secret/microservicio/database
La salida mostrará tanto los metadatos (versión, fecha de creación) como los datos del secreto.
Método de autenticación AppRole
En aplicaciones reales no utilizamos el token raíz. AppRole es un método de autenticación diseñado para aplicaciones y servicios automatizados. Funciona con dos componentes:
- Role ID: Identificador público del rol (similar a un username)
- Secret ID: Credencial secreta (similar a un password)
Habilitamos AppRole:
vault auth enable approle
Creamos una política que define qué puede hacer nuestra aplicación. Guardamos esto en app-policy.hcl
:
path "secret/data/microservicio/*" {
capabilities = ["read"]
}
Aplicamos la política:
vault policy write app-policy app-policy.hcl
Creamos un rol AppRole llamado microservicio-app
:
vault write auth/approle/role/microservicio-app \
secret_id_ttl=24h \
token_ttl=1h \
token_max_ttl=2h \
secret_id_num_uses=0 \
policies="app-policy"
Los parámetros configuran:
- secret_id_ttl: Tiempo de vida del Secret ID (24 horas)
- token_ttl: Duración inicial del token (1 hora)
- token_max_ttl: Duración máxima del token (2 horas)
- secret_id_num_uses: Límite de usos del Secret ID (0 = ilimitado)
Obtención de credenciales AppRole
Recuperamos el Role ID:
vault read auth/approle/role/microservicio-app/role-id
Generamos un Secret ID:
vault write -f auth/approle/role/microservicio-app/secret-id
El output nos proporcionará tanto el Role ID como el Secret ID que necesitaremos para configurar nuestra aplicación Spring Boot.
Autenticación con AppRole
Para probar la autenticación, utilizamos ambas credenciales para obtener un token:
vault write auth/approle/login \
role_id="<role-id-obtenido>" \
secret_id="<secret-id-obtenido>"
Este comando devuelve un token cliente con permisos limitados según la política app-policy
. Con este token, podemos acceder únicamente a los secretos bajo la ruta secret/microservicio/
.
Verificación del acceso a secretos
Utilizamos el token cliente para verificar el acceso:
export VAULT_TOKEN="<client-token-obtenido>"
vault kv get secret/microservicio/database
Si intentamos acceder a rutas no autorizadas, Vault denegará el acceso:
vault kv get secret/otro-microservicio/config
# Error: permission denied
Esta configuración proporciona la base de seguridad necesaria para que nuestros microservicios accedan a secretos de forma controlada. En la siguiente sección veremos cómo integrar esta configuración con Spring Cloud Vault para automatizar la recuperación de secretos durante el arranque de la aplicación.
Spring Cloud Vault: integración básica
Spring Cloud Vault proporciona integración automática entre aplicaciones Spring Boot y HashiCorp Vault, eliminando la necesidad de gestionar manualmente la autenticación y recuperación de secretos. Esta integración utiliza el ConfigData API de Spring Boot 3 para cargar secretos como propiedades de configuración durante el arranque de la aplicación.
A diferencia de Spring Cloud Config que vimos anteriormente, Spring Cloud Vault se especializa en gestión segura de secretos con capacidades adicionales como rotación automática de credenciales y múltiples métodos de autenticación.
Configuración de dependencias
Para integrar Spring Cloud Vault en nuestro proyecto haremos lo siguiente.
Como ya venimos de crear una aplicación Spring Boot con Spring Cloud Config Server ya tenemos un servidor de configuración, lo que haremos es agregar la siguiente dependencia a esa aplicación config server para que pueda comunicarse con el Vault dockerizado.
Por lo que añadimos la dependencia correspondiente al pom.xml
:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
Esta dependencia incluye automáticamente el cliente Vault y las funcionalidades de auto-configuración necesarias para Spring Boot.
Ejemplo completo de cómo quedaría el pom de la aplicación config server con vault:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2025.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
ConfigData API vs Bootstrap Context
Spring Cloud Vault 3.0+ utiliza el ConfigData API de Spring Boot, reemplazando el contexto bootstrap tradicional. Esto significa que la configuración se realiza principalmente en application.yml
en lugar de bootstrap.yml
.
Para habilitar la integración con Vault, configuramos la propiedad spring.config.import
en nuestro application.yml
:
spring:
application:
name: microservicio-demo
config:
import: vault://
cloud:
vault:
uri: http://localhost:8200
authentication: APPROLE
app-role:
role-id: ${VAULT_ROLE_ID}
secret-id: ${VAULT_SECRET_ID}
La propiedad spring.config.import: vault://
instruye a Spring Boot para importar configuración desde Vault durante la fase de inicialización.
Configuración con autenticación AppRole
Configuramos la autenticación AppRole utilizando las credenciales que obtuvimos en la sección anterior. Creamos variables de entorno para evitar hardcodear los valores sensibles:
export VAULT_ROLE_ID="tu-role-id-aqui"
export VAULT_SECRET_ID="tu-secret-id-aqui"
El archivo application.yml
completo sería:
spring:
application:
name: microservicio-demo
config:
import: vault://
cloud:
vault:
uri: http://localhost:8200
authentication: APPROLE
app-role:
role-id: ${VAULT_ROLE_ID}
secret-id: ${VAULT_SECRET_ID}
kv:
enabled: true
backend: secret
default-context: microservicio
Configuración de propiedades específicas
Spring Cloud Vault permite configurar múltiples aspectos del comportamiento de integración:
backend
: Define el motor de secretos a utilizar (por defectosecret
)default-context
: Establece el contexto base para buscar secretos (por defecto el nombre de la aplicación)enabled
: Habilita o deshabilita la integración KV (por defectotrue
)
Inyección automática de propiedades
Una vez configurado, los secretos almacenados en Vault se cargan automáticamente como propiedades de Spring. Si tenemos el secreto almacenado en secret/microservicio/database
, podemos acceder a él mediante:
@Component
public class DatabaseConfig {
@Value("${username}")
private String dbUsername;
@Value("${password}")
private String dbPassword;
@Value("${url}")
private String dbUrl;
// Getters y lógica de configuración
public void printConfig() {
System.out.println("Database URL: " + dbUrl);
System.out.println("Username: " + dbUsername);
// No imprimas la password en logs reales
}
}
Configuration Properties con Vault
Para una aproximación más estructurada, utilizamos @ConfigurationProperties
:
@ConfigurationProperties(prefix = "database")
@Component
public class DatabaseProperties {
private String username;
private String password;
private String url;
// Constructores, getters y setters
public DatabaseProperties() {}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
Esta clase se vincula automáticamente con los secretos almacenados bajo la ruta secret/microservicio/database
donde cada clave (username
, password
, url
) se mapea a las propiedades correspondientes.
Verificación de la integración
Para verificar que la integración funciona correctamente, creamos un endpoint de prueba:
@RestController
public class ConfigTestController {
private final DatabaseProperties databaseProperties;
public ConfigTestController(DatabaseProperties databaseProperties) {
this.databaseProperties = databaseProperties;
}
@GetMapping("/config/test")
public Map<String, String> testConfig() {
Map<String, String> config = new HashMap<>();
config.put("username", databaseProperties.getUsername());
config.put("url", databaseProperties.getUrl());
// Nunca expongas passwords en endpoints reales
config.put("password", "***HIDDEN***");
return config;
}
}
Configuración de múltiples rutas
Spring Cloud Vault permite configurar múltiples rutas de secretos para una misma aplicación:
spring:
cloud:
vault:
generic:
enabled: true
backend: secret
default-context: microservicio
contexts:
- database
- external-apis
- certificates
Con esta configuración, Vault buscará secretos en:
secret/microservicio/database
secret/microservicio/external-apis
secret/microservicio/certificates
Alternativa con token directo
Para entornos de desarrollo o testing, podemos utilizar autenticación por token:
spring:
cloud:
vault:
uri: http://localhost:8200
authentication: TOKEN
token: ${VAULT_TOKEN:dev-root-token}
Esta configuración es útil para pruebas rápidas pero no se recomienda para entornos productivos debido a las implicaciones de seguridad del token raíz.
Gestión de errores y fallback
Spring Cloud Vault proporciona mecanismos de fallback cuando Vault no está disponible:
spring:
cloud:
vault:
fail-fast: false
config:
lifecycle:
enabled: true
fail-fast: false
: Permite que la aplicación arranque aunque Vault no esté disponiblelifecycle.enabled: true
: Habilita la gestión del ciclo de vida para rotación de tokens
Con esta configuración básica, nuestra aplicación Spring Boot puede recuperar automáticamente secretos de Vault durante el arranque, utilizando la autenticación AppRole que configuramos anteriormente. Los secretos se integran transparentemente en el sistema de propiedades de Spring, permitiendo su uso a través de @Value
o @ConfigurationProperties
.
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en SpringBoot
Documentación oficial de SpringBoot
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, SpringBoot 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 SpringBoot
Explora más contenido relacionado con SpringBoot y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender la arquitectura y funcionamiento básico de HashiCorp Vault, especialmente el motor KV v2.
- Configurar un entorno de desarrollo Vault con Docker y almacenar secretos de forma segura.
- Implementar el método de autenticación AppRole para aplicaciones automatizadas.
- Integrar Spring Cloud Vault en aplicaciones Spring Boot para la recuperación automática de secretos.
- Configurar y utilizar propiedades inyectadas desde Vault en aplicaciones Spring mediante @Value y @ConfigurationProperties.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje