Skip to content

Commit 6bfffce

Browse files
authored
feat(vault): implement advanced secret management with hashicorp vault (#19)
* infra(vault): add hashicorp vault to docker compose stack * feat(all): integrate spring cloud vault dependencies * feat(all): configure vault bootstrap connection * refactor(all): source all application secrets from vault * refactor(security): source actuator role from configuration * fix(security): override nimbus-jose-jwt to patch transitive vulnerability * docs(readme): update main README for phase 8 completion * fix(ci): adapt dast scan script for vault integration
1 parent 2f29e13 commit 6bfffce

File tree

18 files changed

+479
-83
lines changed

18 files changed

+479
-83
lines changed

.env.ci

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
# scan's subset of services (postgres, keycloak, resource-server) to initialize.
66
# ===================================================================
77

8-
# Actuator basic auth configuration
9-
ACTUATOR_USERNAME=actuator
10-
ACTUATOR_PASSWORD=actuator-password
11-
ACTUATOR_ROLES=ACTUATOR_ADMIN
8+
# Prometheus Scrape Credentials
9+
PROMETHEUS_SCRAP_USER=actuator
10+
PROMETHEUS_SCRAP_PASSWORD=actuator-password
1211

1312
# Keycloak Admin Credentials
1413
KC_BOOTSTRAP_ADMIN_USERNAME=admin

.env.example

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
# These variables will be used by docker-compose.yml to configure the services.
88
# ===================================================================
99

10-
# Actuator basic auth configuration
11-
ACTUATOR_USERNAME=actuator
12-
ACTUATOR_PASSWORD=actuator-password
13-
ACTUATOR_ROLES=ACTUATOR_ADMIN
10+
# Prometheus Scrape Credentials
11+
PROMETHEUS_SCRAP_USER=actuator
12+
PROMETHEUS_SCRAP_PASSWORD=actuator-password
1413

1514
# Keycloak Admin Credentials
1615
KC_BOOTSTRAP_ADMIN_USERNAME=admin
@@ -19,7 +18,4 @@ KC_BOOTSTRAP_ADMIN_PASSWORD=admin
1918
# PostgreSQL Credentials for Keycloak
2019
POSTGRES_DB=keycloak
2120
POSTGRES_USER=keycloak
22-
POSTGRES_PASSWORD=keycloak-password
23-
24-
# Keycloak Web Client Credentials
25-
WEB_CLIENT_SECRET=replace-with-web-app-client-secret-from-keycloak
21+
POSTGRES_PASSWORD=keycloak-password

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Desktop.ini
4646
### Lab Specific ###
4747
# Local environment variables
4848
.env
49+
.secrets.env
4950

5051
# Persistent data volumes
5152
/data/

.secrets.env.ci

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# ===================================================================
2+
# CI/CD APPLICATION SECRETS FOR VAULT
3+
# ===================================================================
4+
# This file contains dummy/test secrets for populating Vault during CI/CD runs.
5+
# These values are used by the `vault-setup` init container.
6+
# This file is committed to Git, so DO NOT put real secrets here.
7+
# ===================================================================
8+
9+
# Actuator basic auth credentials for Spring Boot applications
10+
ACTUATOR_USERNAME=ci-actuator
11+
ACTUATOR_PASSWORD=ci-actuator-password
12+
ACTUATOR_ROLES=CI_ACTUATOR_ADMIN
13+
14+
# Keycloak Web Client Credentials
15+
WEB_CLIENT_SECRET=ci-web-client-secret-value

.secrets.env.example

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# ===================================================================
2+
# APPLICATION SECRETS FOR VAULT
3+
# ===================================================================
4+
# Copy this file to .secrets.env and populate it.
5+
# This file contains the secrets that will be written into HashiCorp Vault
6+
# by the `scripts/populate-vault.sh` script.
7+
#
8+
# The .secrets.env file is ignored by Git and should NEVER be committed.
9+
# ===================================================================
10+
11+
# Actuator basic auth credentials for Spring Boot applications
12+
ACTUATOR_USERNAME=actuator
13+
ACTUATOR_PASSWORD=actuator-password
14+
ACTUATOR_ROLES=ACTUATOR_ADMIN
15+
16+
# Keycloak Web Client Credentials
17+
WEB_CLIENT_SECRET=a-very-strong-and-long-secret-for-the-web-client

README.md

Lines changed: 171 additions & 28 deletions
Large diffs are not rendered by default.

docker-compose.yml

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ services:
3030
retries: 10
3131
start_period: 20s
3232
depends_on:
33+
vault-setup:
34+
condition: service_completed_successfully
3335
keycloak:
3436
condition: service_healthy
37+
vault:
38+
condition: service_healthy
3539
alloy:
3640
condition: service_started
3741
networks:
@@ -61,10 +65,14 @@ services:
6165
# Mount the agent JAR into a known location in the container.
6266
- ./config/otel/opentelemetry-javaagent.jar:/app/opentelemetry-javaagent.jar:ro
6367
depends_on:
68+
vault-setup:
69+
condition: service_completed_successfully
6470
keycloak:
6571
condition: service_healthy
6672
resource-server:
6773
condition: service_healthy
74+
vault:
75+
condition: service_healthy
6876
alloy:
6977
condition: service_started
7078
networks:
@@ -142,6 +150,55 @@ services:
142150
networks:
143151
- lab-net
144152

153+
# --------------------------------------------------------------------------
154+
# Secrets Management Service: HashiCorp Vault
155+
# --------------------------------------------------------------------------
156+
vault:
157+
image: "hashicorp/vault:1.20.3"
158+
container_name: vault
159+
ports:
160+
- "8200:8200"
161+
environment:
162+
# Root token for the dev server
163+
VAULT_DEV_ROOT_TOKEN_ID: "dev-root-token"
164+
# Target address for the vault CLI tool inside the container
165+
VAULT_ADDR: "http://127.0.0.1:8200"
166+
# Auth token for the vault CLI, enabling `docker exec` commands
167+
VAULT_TOKEN: "dev-root-token"
168+
cap_add:
169+
# Grants mlock capability to prevent secrets from being swapped to disk
170+
- IPC_LOCK
171+
command: "server -dev"
172+
healthcheck:
173+
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:8200/v1/sys/health || exit 1"]
174+
interval: 5s
175+
timeout: 2s
176+
retries: 5
177+
start_period: 5s
178+
networks:
179+
- lab-net
180+
181+
# --------------------------------------------------------------------------
182+
# Vault Setup Service
183+
# --------------------------------------------------------------------------
184+
vault-setup:
185+
image: "hashicorp/vault:1.20.3"
186+
container_name: vault-setup
187+
volumes:
188+
# Mount the script into the container to be executed
189+
- ./scripts/populate-vault.sh:/usr/local/bin/populate-vault.sh:ro
190+
environment:
191+
- VAULT_ADDR=http://vault:8200
192+
- VAULT_TOKEN=dev-root-token
193+
env_file:
194+
- .secrets.env
195+
command: "sh /usr/local/bin/populate-vault.sh"
196+
depends_on:
197+
vault:
198+
condition: service_healthy
199+
networks:
200+
- lab-net
201+
145202
# --------------------------------------------------------------------------
146203
# Prometheus Service: For metrics collection.
147204
# --------------------------------------------------------------------------
@@ -266,9 +323,9 @@ services:
266323

267324
secrets:
268325
actuator_username:
269-
environment: "ACTUATOR_USERNAME"
326+
environment: "PROMETHEUS_SCRAP_USER"
270327
actuator_password:
271-
environment: "ACTUATOR_PASSWORD"
328+
environment: "PROMETHEUS_SCRAP_PASSWORD"
272329

273330
volumes:
274331
grafana-data:

pom.xml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,17 @@
2121
<properties>
2222
<java.version>21</java.version>
2323
<spring-boot.version>3.5.5</spring-boot.version>
24+
<spring-cloud.version>2025.0.0</spring-cloud.version>
2425
<!--
2526
Explicitly overriding Netty version to mitigate CVE-2025-58056.
2627
https://nvd.nist.gov/vuln/detail/CVE-2025-58056
27-
This forces all transitive Netty dependencies to use a patched version.
2828
-->
2929
<netty.version>4.1.126.Final</netty.version>
30+
<!--
31+
Explicitly overriding Netty version to mitigate CVE-2025-53864.
32+
https://nvd.nist.gov/vuln/detail/CVE-2025-53864
33+
-->
34+
<nimbus-jose-jwt.version>9.37.4</nimbus-jose-jwt.version>
3035
<logstash-logback-encoder.version>8.1</logstash-logback-encoder.version>
3136
<lombok.version>1.18.38</lombok.version>
3237
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
@@ -46,13 +51,28 @@
4651
<type>pom</type>
4752
<scope>import</scope>
4853
</dependency>
54+
<!--
55+
SECURITY REMEDIATION (CVE-2025-53864)
56+
-->
57+
<dependency>
58+
<groupId>com.nimbusds</groupId>
59+
<artifactId>nimbus-jose-jwt</artifactId>
60+
<version>${nimbus-jose-jwt.version}</version>
61+
</dependency>
4962
<dependency>
5063
<groupId>org.springframework.boot</groupId>
5164
<artifactId>spring-boot-dependencies</artifactId>
5265
<version>${spring-boot.version}</version>
5366
<type>pom</type>
5467
<scope>import</scope>
5568
</dependency>
69+
<dependency>
70+
<groupId>org.springframework.cloud</groupId>
71+
<artifactId>spring-cloud-dependencies</artifactId>
72+
<version>${spring-cloud.version}</version>
73+
<type>pom</type>
74+
<scope>import</scope>
75+
</dependency>
5676

5777
<!-- Other managed dependencies -->
5878
<dependency>

resource-server/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
2929
</dependency>
3030

31+
<!-- Vault integration -->
32+
<dependency>
33+
<groupId>org.springframework.cloud</groupId>
34+
<artifactId>spring-cloud-starter-vault-config</artifactId>
35+
</dependency>
36+
3137
<!-- API Documentation -->
3238
<dependency>
3339
<groupId>org.springdoc</groupId>

resource-server/src/main/java/com/apenlor/lab/resourceserver/config/SecurityConfig.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.apenlor.lab.resourceserver.config;
22

33
import com.apenlor.lab.resourceserver.auth.KeycloakRoleConverter;
4+
import org.springframework.beans.factory.annotation.Value;
45
import org.springframework.context.annotation.Bean;
56
import org.springframework.context.annotation.Configuration;
67
import org.springframework.core.annotation.Order;
@@ -33,6 +34,9 @@ public class SecurityConfig {
3334
"/swagger-ui.html"
3435
};
3536

37+
@Value("${actuator.roles}")
38+
private String actuatorAdminRole;
39+
3640
/**
3741
* Configures the security filter chain for the management endpoints (Actuator).
3842
* <p>
@@ -51,7 +55,7 @@ public SecurityFilterChain actuatorSecurityFilterChain(HttpSecurity http) throws
5155
.authorizeHttpRequests(authorize -> authorize
5256
// Allow anonymous access to health and info endpoints.
5357
.requestMatchers("/actuator/health", "/actuator/info").permitAll()
54-
.anyRequest().hasRole("ACTUATOR_ADMIN")
58+
.anyRequest().hasRole(actuatorAdminRole)
5559
)
5660
.httpBasic(Customizer.withDefaults())
5761
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

0 commit comments

Comments
 (0)