Comprehensive Guide to Using HashiCorp Vault in a Spring Boot Environment
Table of Contents
Vault's Role in Production with Spring Boot
System Architecture Overview
Authentication and Security Risks
Vault Agent and Secure Integration
Vault Configuration for Storing Secrets and HTTPS Setup
Vault Configuration for the Transit Engine (Encryption and Decryption)
Handling the Vault Token in Spring Boot
Conclusion
What is HashiCorp Vault?
HashiCorp Vault is an open-source tool designed to manage secrets and protect sensitive data. It provides centralized secret storage, access control, and audit logging. Vault integrates seamlessly with various cloud platforms, allowing it to secure sensitive data in both cloud-native and on-premise environments.
Vault's Role in Production with Spring Boot
In production, Vault ensures that secrets such as database credentials are managed dynamically, removing the need for hardcoded sensitive information in the application.
Benefits of HashiCorp Vault for Secret Management
- Centralized Secret Storage: Prevents hardcoded secrets in code, reducing the attack surface.
- Dynamic Credentials: Generates time-limited credentials, minimizing security risks.
- Auditing and Compliance: Built-in audit logs help track access and meet regulatory requirements.
System Architecture Overview
Components of the Architecture
A typical Vault production setup with Spring Boot involves:
- Spring Boot Application: The main Java-based microservices framework.
- Vault Agent: Intermediary on the production server where Spring Boot runs, fetching secrets from the Vault Server.
- Vault Server: Manages and stores secrets securely.
- SQL Database: Backend that holds application data, requiring secure access through dynamic credentials.
Secrets Flow Diagram
The following steps illustrate how secrets are retrieved and used:
- Spring Boot requests credentials from the Vault Agent.
- The Vault Agent authenticates to the Vault Server and retrieves the required secrets.
- The credentials are securely injected into the application, which then connects to the SQL database.
Authentication and Security Risks
Credential Storage Risks
Storing usernames and passwords in plain text or configuration files can expose them to attackers, posing a significant security risk.
Mitigation Strategies
- Avoid Static Credentials: Use dynamic credentials instead of hardcoded usernames and passwords.
- AppRole Authentication: Vault’s AppRole method allows non-interactive authentication with dynamic tokens, improving security.
- Secret Wrapping: Vault can wrap secrets in a time-limited token to securely pass credentials between systems.
Vault Agent and Secure Integration
Configuring Vault Agent for a Production Environment
In a production environment, the Vault Agent is deployed on the same server as the Spring Boot application, while the Vault Server resides on a separate machine. All communications between the Vault Agent, Vault Server, and the SQL Database are secured using TLS encryption.
Environments Setup Overview:
- Vault Server (IP: 192.168.101.1): Manages secrets and provides them securely to authorized applications.
- Vault Agent (IP: 192.168.101.2): Intermediary on the production server where Spring Boot runs, fetching secrets from the Vault Server.
- SQL Database (IP: 192.168.101.3): Stores application data, accessed via credentials managed by Vault.
- TLS Encryption: Secures communication between Vault components to ensure data privacy and integrity.

Environment overview

Vault Agent Configuration Example:
Here’s how you would configure the Vault Agent on the production server (192.168.101.2) to securely retrieve secrets from the Vault Server (192.168.101.1) over TLS.
auto_auth {
method "approle" {
mount_path = "auth/approle"
config = {
role_id_file_path = "/etc/vault/role_id"
secret_id_file_path = "/etc/vault/secret_id"
}
}
}
cache {
use_auto_auth_token = true
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/etc/vault-agent/certs/vault-agent.crt"
tls_key_file = "/etc/vault-agent/certs/vault-agent.key"
tls_client_ca_file = "/etc/vault-agent/certs/ca.crt"
}
vault {
address = "https://192.168.101.1:8200"
}
Spring Boot Integration in Production
The Spring Boot application running on 192.168.101.2 communicates with the Vault Agent to retrieve dynamic credentials securely.
Example: Application Properties for Vault Integration:
spring:
cloud:
vault:
uri: https://127.0.0.1:8200 # Vault Agent local communication
authentication: APPROLE
token: ${VAULT_TOKEN}
Vault Configuration for Storing Secrets and HTTPS Setup
To store secrets such as database credentials in Vault and retrieve them securely in a Spring Boot application, follow these steps:
Step 1: Enable KV Secrets Engine
Step 2: Store Secrets
Step 3: Configuring Spring Boot to Retrieve Secrets
spring:
cloud:
vault:
kv:
enabled: true
uri: https://192.168.101.1:8200
authentication: APPROLE
token: ${VAULT_TOKEN}
kv:
backend: secret
Enabling HTTPS on Vault
To enable HTTPS and secure communication between Vault, Vault Agent, and Spring Boot, you need to configure Vault with SSL certificates.
Step 1: Generate SSL Certificates
Step 2: Configure Vault for HTTPS
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/etc/vault/vault.crt"
tls_key_file = "/etc/vault/vault.key"
}
storage "file" {
path = "/vault/data"
}
Step 3: Start Vault with HTTPS
Vault Configuration for the Transit Engine (Encryption and Decryption)
The Vault
Transit Secrets Engine is designed for cryptographic operations like
encrypting and decrypting data. Below are the steps to configure
Vault to handle encryption and decryption using the my-encryption-key
.
Step 1: Enable the Transit Secrets Engine
To enable the Transit Secrets Engine, you’ll use the Vault CLI. This engine is responsible for managing encryption keys and performing cryptographic operations like encrypting and decrypting data.
This command enables the Transit
Secrets Engine at the default path, transit/
. If you
want to enable it at a custom path, you can specify that with the -path
flag, but for simplicity, we’ll stick with the default.
Step 2: Create an Encryption Key
After enabling the Transit
engine, the next step is to create an encryption key named my-encryption-key
.
This key will be used by the Spring Boot application to encrypt and
decrypt data.
This command creates a
named encryption key (my-encryption-key
) under the Transit
engine. By default, the key is generated with 256-bit
AES-GCM encryption. You can customize the key type by specifying
additional parameters if needed.
Step 3: Configure Key Permissions
To make sure the key is usable for both encryption and decryption, you need to configure the appropriate permissions. Vault handles key management and automatically tracks how the key is used.
- Enable Encryption: The key is ready to encrypt data once created.
- Enable Decryption: By default, Vault enables both encryption and decryption.
You can verify the key’s status and configuration with:
Step 4: Use the Key for Encryption and Decryption
Encrypt Data
To encrypt data using the my-encryption-key
:
curl --header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data '{"plaintext": "VGhpcyBpcyBhIHNlY3JldCBtZXNzYWdl"}' \
https://192.168.101.1:8200/v1/transit/encrypt/my-encryption-key
In the above request:
- The
plaintext
is the base64-encoded data you want to encrypt (VGhpcyBpcyBhIHNlY3JldCBtZXNzYWdl
is "This is a secret message" in Base64). - The Vault server responds with a ciphertext string.
Decrypt Data
To decrypt the data using the same key:
curl --header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data '{"ciphertext": "vault:v1:i8NjUu..."}' \
https://192.168.101.1:8200/v1/transit/decrypt/my-encryption-key
In the above request:
- The
ciphertext
is the encrypted string returned by Vault during the encryption step.
Step 5: Implement Vault Encryption and Decryption in Spring Boot
Once the Vault server is configured, you can integrate the encryption and decryption in your Spring Boot application. Here's the Java code (previously provided) for encrypting and decrypting data:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class VaultEncryptionService {
private RestTemplate restTemplate;
private static final String VAULT_TRANSIT_ENCRYPT_URL =
"https://192.168.101.1:8200/v1/transit/encrypt/my-encryption-key";
private static final String VAULT_TRANSIT_DECRYPT_URL =
"https://192.168.101.1:8200/v1/transit/decrypt/my-encryption-key";
// Encrypt Data
public String encryptData(String plaintext) {
Map<String, String> requestBody = new HashMap<>();
requestBody.put("plaintext", Base64.getEncoder().encodeToString(plaintext.getBytes()));
try {
ResponseEntity<Map<String, Object>> response = restTemplate.postForEntity(
VAULT_TRANSIT_ENCRYPT_URL, requestBody, Map.class);
if (response.getBody() != null && response.getBody().containsKey("ciphertext")) {
return (String) response.getBody().get("ciphertext");
} else {
throw new RuntimeException("Encryption failed: no ciphertext returned");
}
} catch (HttpClientErrorException e) {
throw new RuntimeException("Error during encryption request: " + e.getResponseBodyAsString(), e);
}
}
// Decrypt Data
public String decryptData(String ciphertext) {
Map<String, String> requestBody = new HashMap<>();
requestBody.put("ciphertext", ciphertext);
try {
ResponseEntity<Map<String, Object>> response = restTemplate.postForEntity(
VAULT_TRANSIT_DECRYPT_URL, requestBody, Map.class);
if (response.getBody() != null && response.getBody().containsKey("plaintext")) {
String base64Decrypted = (String) response.getBody().get("plaintext");
return new String(Base64.getDecoder().decode(base64Decrypted));
} else {
throw new RuntimeException("Decryption failed: no plaintext returned");
}
} catch (HttpClientErrorException e) {
throw new RuntimeException("Error during decryption request: " + e.getResponseBodyAsString(), e);
}
}
}
Handling the Vault Token in Spring Boot
In this configuration: The Vault Agent authenticates using AppRole with the role_id and secret_id files.
The token_file parameter points to where the token will be stored after successful authentication (/etc/vault/token).
The token is cached and renewed automatically, ensuring it’s always available without manual intervention.
Injecting the Token into Spring Boot:
Once the Vault Agent retrieves the token and writes it to the token_file, you can configure Spring Boot to read the token from this file or directly from the environment:
This makes the
VAULT_TOKEN
available as an environment variable.
2. Manually Setting the Vault Token as an Environment Variable
You can also export the Vault token directly as an environment variable when deploying your application. For example, in your CI/CD pipeline or server deployment script:
This way, the token is securely passed to the Spring Boot application during startup.
Step 2:
Injecting the VAULT_TOKEN
into Spring Boot Properties
In your Spring Boot
application, you can reference the VAULT_TOKEN
environment variable inside your application.yml
or application.properties
file:
spring:
cloud:
vault:
uri: https://192.168.101.1:8200
authentication: APPROLE
token: ${VAULT_TOKEN}
Spring Boot will
automatically resolve the ${VAULT_TOKEN}
placeholder by
reading the value from the environment variable that was exported
during deployment or managed by the Vault Agent.
Step 3: Testing the Configuration
To verify that the VAULT_TOKEN
is properly injected into the application, you can test by running:
If the token is set correctly, it will print the token string, and Spring Boot will use it to authenticate with Vault.
Handling Token Renewal and Expiration
If using the Vault Agent, the token is automatically renewed before expiration, thanks to the auto-auth and cache mechanisms. If you’re managing the token manually, ensure that the token is regularly renewed by your CI/CD or deployment process, as Vault tokens have a time-to-live (TTL).
Challenges and Considerations in Production:
Conclusion
Integrating HashiCorp Vault with Spring Boot ensures secure and centralized secrets management in production environments. By using Vault's dynamic credential generation and encryption capabilities, developers can reduce the risk of exposing sensitive data while enhancing the security of their applications.
References
- HashiCorp Vault Documentation
- Spring Cloud Vault Documentation
- Database Secrets Engine in Vault
- Essential HashiCorp Vault Guide and Commands
- Setting Up HashiCorp Vault on Ubuntu: A Beginner's Guide
- Illustrations generated with the help of OpenAI's AI.
About Me
I am passionate about IT technologies. If you’re interested in learning more or staying updated with my latest articles, feel free to connect with me on:
LinkedIn
Instagram
Personal Website
Feel free to reach out through any of these platforms if you have any questions!
I am excited to hear your thoughts! 👇
Comments
Post a Comment