Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b6tTs7ivgTNq4ZrurU
secrets
Companion code for Secrets in Spring Boot: Vault vs AWS Secrets Manager vs Kubernetes Secrets vs Jasypt on ankurm.com.
One small application that needs one database password, started nine different ways. Every
backend is exercised by a test, and each test writes the transcript the article quotes to output/.
There is no docs/ folder: the explanations live in the article.
| Piece | Version | Checked against |
|---|---|---|
| Spring Boot | 4.1.2 | maven-metadata.xml for spring-boot-starter-parent |
| Spring Cloud | 2025.1.4 | maven-metadata.xml (2026.0.0 is RC only); 2025.1.2 added Boot 4.1 support per the release announcement |
| Spring Cloud Vault | 5.0.3 (pulls spring-vault-core 4.0.4) |
mvn dependency:tree |
| Spring Cloud AWS | 4.2.1 (AWS SDK 2.57.0) | maven-metadata.xml |
| jasypt-spring-boot | 4.0.4 | maven-metadata.xml |
| Testcontainers | 2.0.5 | managed by Spring Boot 4.1.2 |
| Vault server | 2.2.1 (hashicorp/vault:2.2.1, dev mode) |
vault version in the container |
| AWS emulator | moto 5.2.5 (motoserver/moto:5.2.5) |
not AWS: no IAM, KMS or rotation Lambdas |
| JDK | 25 | Temurin 25.0.5 |
Run it
export JAVA_HOME=/path/to/jdk-25
./scripts/run-all.sh # 9 tests (needs Docker), then the jar and /proc script
What is where
| Transcript | Test or script | What it shows |
|---|---|---|
output/01-vault-import.txt |
VaultImportTest |
spring.config.import=vault:// binding a KV v2 secret, and the property source it creates |
output/02-vault-missing-and-denied.txt |
VaultImportTest |
missing path, wrong token, unreachable Vault, fail-fast, optional: and the strict startup guard |
output/03-vault-rotation.txt |
VaultRotationTest |
a rotated secret, ContextRefresher, record vs @RefreshScope |
output/04-vault-least-privilege.txt |
VaultRotationTest, vault/app-read.hcl |
a token that can read two paths and nothing else |
output/05-aws-secrets-manager.txt |
AwsSecretsManagerTest |
JSON secret import, version stages, polling reload, missing secret |
output/06-kubernetes-configtree.txt |
KubernetesConfigtreeTest, k8s/*.yaml |
the kubelet's volume layout read by configtree:, and a swap |
output/07-jasypt.txt |
JasyptTest |
ENC(...), wrong and missing keys, decrypting outside Spring |
output/08-leaks.txt |
LeaksTest |
record toString, actuator env and configprops |
output/09-dynamic-property-source-trap.txt |
DynamicPropertyTrapTest |
why a Testcontainers @DynamicPropertySource does not reach the import |
output/10-exposure.txt |
scripts/exposure.sh |
a secret in the jar, in ps, in /proc/<pid>/environ |
k8s/secret.yaml and k8s/deployment-snippet.yaml were not applied to a cluster; the volume
layout they produce is reproduced by hand in KubernetesConfigtreeTest. Timing figures and the AWS
polling result are indicative.