The Lost Art of Keeping a Secret
Ever noticed how Renaissance artists loved slipping hidden symbols into their work? A tiny skull in a still life, a subtle gesture hinting at a deeper story—these details weren’t just decorative. They were messages, meant to be seen but not always understood. Only those who knew what to look for could decode their true meaning.
Fast forward to today, and we’re still embedding secrets—only now, they live in code instead of canvases. When we build applications with Spring Boot, we have to handle sensitive information like API keys, database credentials, and configuration secrets. These aren’t meant to be out in the open, but they’re still there, quietly waiting beneath the surface.
That’s where HashiCorp Vault comes in. Just like an art historian needs the right knowledge to uncover a painting’s hidden story, an application needs the right authentication to unlock its secrets. Vault ensures that only the right people (or systems) get access, keeping everything else locked away.
Because whether it’s a Renaissance painting or modern software, some things are meant to stay hidden - until the right eyes find them.
Let’s dive into the practical part: how do you actually bring this beautiful art together? How does Spring Boot integrate seamlessly with Vault?
Step 1: Set up Vault
HashiCorp Vault is used to manage and access secrets. You'll run Vault in a Docker container for local development. Ensure Docker is installed and running on your machine.
1 #sh
2
3 docker pull vault:latest
4
1 #sh
2
3 docker run -d --name vault -p 8200:8200 -e 'VAULT_DEV_ROOT_TOKEN_ID=root' -e 'VAULT_ADDR=http://127.0.0.1:8200' hashicorp/vault:latest
4
This will start Vault in development mode with the root token root for access. You can access Vault at http://localhost:8200.
Step 2: Add a secret
To create a secret in Vault, follow these steps:
1 #sh
2
3 docker exec -it vault bin/sh
4
1 #sh
2
3 export VAULT_TOKEN=root
4
1 #sh
2
3 vault kv put secret/payment-gateway api-key="super-secret-api-key"
4
This stores the secret under the path secret/payment-gateway.
Alright, so here it is essential to be super careful with the versions compatibility. If you want to use newer Spring Boot/Spring Cloud versions, it's a must to check the compatibility matrix before diving into it (below there's a working config).
Next, we’ll set up our Spring Boot application to interact with Vault.
1 <!-- xml -->
2 <parent>
3 <groupId>org.springframework.boot</groupId>
4 <artifactId>spring-boot-starter-parent</artifactId>
5 <version>3.2.2</version>
6 <relativePath/> <!-- lookup parent from repository -->
7 </ parent >
8 <!-- ... -->
9 <properties>
10 <java.version>21</java.version>
11 </properties>
12
13 <dependencyManagement>
14 <dependencies>
15 <!-- Spring Cloud BOM (2023.x) compatible with Spring Boot 3.x -->
16 <dependency>
17 <groupId>org.springframework.cloud</groupId>
18 <artifactId>spring-cloud-dependencies</artifactId>
19 <version>2023.0.0</version> <!-- Ensure you're using the right BOM -->
20 <scope>import</scope>
21 <type>pom</type>
22 </dependency>
23 </dependencies>
24 </dependencyManagement>
25
26 <dependencies>
27 <!-- Spring Cloud Vault dependency -->
28 <dependency>
29 <groupId>org.springframework.cloud</groupId>
30 <artifactId>spring-cloud-starter-vault-config</artifactId>
31 </dependency>
32
33 <!-- Spring Boot starter for web, if needed -->
34 <dependency>
35 <groupId>org.springframework.boot</groupId>
36 <artifactId>spring-boot-starter-web</artifactId>
37 </dependency>
38 </dependencies>
39
40 <build>
41 <plugins>
42 <plugin>
43 <groupId>org.springframework.boot</groupId>
44 <artifactId>spring-boot-maven-plugin</artifactId>
45 </plugin>
46 </plugins>
47 </build>
48
In src/main/resources/application.properties, configure Spring Boot to connect to Vault:
1 properties
2 spring.cloud.vault.application-name=payment-gateway
3 spring.cloud.vault.uri=http://127.0.0.1:8200
4 spring.cloud.vault.authentication=TOKEN
5 spring.cloud.vault.token=root
6 spring.cloud.vault.kv.enabled=true
7 spring.cloud.vault.kv.backend=secret
8 spring.config.import=vault://
9
This configuration tells Spring Boot where to find Vault, which token to use, and where to look for the secret.
Step 4: Request the Secret
1 //java
2 @RestController
3 public class DemoController {
4
5 @Value("${api-key}")
6 private String apiKey;
7
8 @GetMapping("/payment")
9 public String getVaultSecrets() {
10 return "API Key: " + apiKey;
11 }
12 }
13
Here, Spring Boot uses the @Value annotation to inject the API key from Vault into the apiKey variable.
Finally, run your Spring Boot application, and visit the /payment endpoint:
1 #sh
2
3 curl http://localhost:8080/payment
4
5 API Key: super-secret-api-key
6
Voilà! The secret was never exposed directly in your Spring Boot application; it was securely retrieved from Vault when needed.
At the end of the day, keeping secrets in your app is a lot like Renaissance art - some details are meant to stay hidden, and only the right people should get the full picture. With Spring Boot and HashiCorp Vault, you’re basically the museum curator of your own digital masterpiece. Just, you know… with fewer paintbrushes and more security keys.
GitHub project: https://github.com/oceanobe/vault-poc
Bonus Track & Inspiration: Queens of the Stone Age - The Lost Art of Keeping a Secret