Foundation · Configure
Secrets Management & Environment Safety
Passwords, API keys, and tokens come from the environment or a vault—never from files under version control.
Primary source: Spring Boot 3.5 Reference: Externalized Configuration
Lesson 4 of 4
1d streakThe Architectural Question
A credential pushed to a public repository is compromised forever; scanners harvest it within minutes, and deleting the file does not remove it from git history.
What this guide proves
- Reference secrets with ${ENV_VAR} placeholders instead of literals
- Provide non-secret defaults while keeping secrets required
- Scan history and rotate anything that ever leaked
Secrets Management & The Twelve-Factor Methodology
Credentials, private API keys, TLS certificates, and database passwords must never be stored in Git repositories or hardcoded into application properties files. The Twelve-Factor App specification strictly mandates that configuration that varies between deploys (and all secrets) must be injected strictly from the deployment execution environment.
- 011. Never commit plain-text credentials to version control (even in private repos)
- 022. Inject secrets into runtime memory via container environment variables or volume mounts
- 033. Mask and sanitize secrets in logs, metrics, exception traces, and actuator endpoints
- 044. Implement automated secret rotation without requiring application restarts
- 055. Restrict access using least-privilege IAM roles and secret vault engines
The Danger of Git Purging
Active Recall Checkpoint
Why is setting ${DB_PASSWORD:} (with an empty colon fallback) dangerous for production database credentials?
Hint: What happens if the operator forgets to set the DB_PASSWORD environment variable?
Show answer & explanation
The colon fallback ${DB_PASSWORD:} supplies an empty string "" as default. Instead of failing fast at startup with a clear error, the app boots and attempts to connect with an empty password, leading to confusing authentication errors or security bypasses.
Enterprise Secrets: Kubernetes ConfigTree, Vault & Cloud Providers
Modern cloud-native Spring Boot applications leverage specialized secret engines rather than static environment variables. Spring Boot provides native integration via spring.config.import.
| Secrets Mechanism | Spring Boot Integration | Operational Advantage |
|---|---|---|
| Kubernetes Secrets (Mounted Volumes) | spring.config.import=configtree:/etc/secrets/ | Each file in the directory becomes a property key. Kubernetes manages rotation; no cloud SDK required. |
| HashiCorp Vault | Spring Cloud Vault (vault://) | Dynamic credential generation, leased access, and centralized enterprise audit logging. |
| AWS Secrets Manager / Parameter Store | Spring Cloud AWS Secrets Manager | Direct IAM-authenticated retrieval; secrets never touch disk or container environment lists. |
| Encrypted Properties (Jasypt / Age) | Jasypt Spring Boot Starter (ENC(...)) | Enables storing encrypted ciphertext in Git; private decryption key is supplied only at boot time. |
# Reading Kubernetes secret volume mount as a config tree:
spring:
config:
import:
- "configtree:/etc/secrets/database/"
- "configtree:/etc/secrets/stripe/"
datasource:
# Files /etc/secrets/database/username and /etc/secrets/database/password are mapped automatically:
username: "${username}"
password: "${password}"Active Recall Checkpoint
What is the primary security advantage of Kubernetes configtree: volume mounts over container environment variables (env)?
Hint: Can environment variables be read by other processes or crash reports?
Show answer & explanation
Container environment variables can be inspected via /proc/[pid]/environ, Docker inspect, or crash logs. Volume-mounted files in tmpfs exist purely in RAM and are only accessible by the specific process reading that file path.
Actuator Sanitization & Preventing Secret Leakage
Spring Boot Actuator endpoints such as /actuator/env and /actuator/configprops inspect the live Environment. Without explicit configuration, these endpoints could expose database credentials, OAuth client secrets, and internal tokens to unauthorized network callers.
management:
endpoint:
env:
show-values: when_authorized
roles: "SYSTEM_ADMIN"
configprops:
show-values: when_authorized
roles: "SYSTEM_ADMIN"
endpoints:
web:
exposure:
include: "health,info,metrics,prometheus" # Exclude env and configprops from public exposure!By default, Spring Boot 3 sanitizes keys containing password, secret, key, token, credential, vcap_services, sun_java_command, replacing their values with ******. You can configure custom sanitization regexes via management.endpoint.env.keys-to-sanitize.
Never Expose /actuator/env Unauthenticated
/actuator/env publicly is a high-severity security vulnerability (OWASP A05: Security Misconfiguration). Attackers can inspect runtime configuration or exploit property injection endpoints to achieve Remote Code Execution (RCE).Active Recall Checkpoint
What setting ensures that secrets in /actuator/env are masked with asterisks unless accessed by a user with SYSTEM_ADMIN authority?
Hint: Look at the management.endpoint.env.show-values options in Spring Boot.
Show answer & explanation
Set management.endpoint.env.show-values=when_authorized along with configuring the authorized roles via management.endpoint.env.roles=SYSTEM_ADMIN.
Deliberate Failure Lab: Missing Secret Startup Failure
To ensure fail-fast guarantees, never provide silent default fallbacks for security credentials. Reference the environment variable directly without a default: ${DB_PASSWORD}. If the environment variable is not defined, Spring Boot refuses to start.
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'spring.datasource.password' to java.lang.String:
Property: spring.datasource.password
Value: "${DB_PASSWORD}"
Origin: "spring.datasource.password" from property source "application.yml"
Reason: Could not resolve placeholder 'DB_PASSWORD' in value "${DB_PASSWORD}"
Action:
Set the environment variable DB_PASSWORD before starting the application.The Principle of Safe Defaults
Active Recall Checkpoint
How do you configure an optional environment variable with a fallback vs a strictly required one in application.yml?
Hint: Compare the syntax ${KEY} with ${KEY:default}.
Show answer & explanation
${REQUIRED_SECRET} has no colon and fails startup if unset. ${OPTIONAL_VAR:defaultValue} uses everything after the colon as the fallback value if the property is missing.
Runtime Architecture & Flow
Resolution model
Configuration resolution checklist
- 1
Reference secrets with ${ENV_VAR} placeholders instead of literals
- 2
Provide non-secret defaults while keeping secrets required
- 3
Scan history and rotate anything that ever leaked
Inspect the effective application state before assuming which declaration won.
Working Implementation
Code Walkthrough
Replace a committed database password with DB_PASSWORD, run without setting it to see the failure, then supply it via environment.
package com.example.config;
import jakarta.validation.constraints.NotBlank;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.annotation.Validated;
@Configuration
public class SecretsSafetyLab {
private static final Logger log = LoggerFactory.getLogger(SecretsSafetyLab.class);
@ConfigurationProperties(prefix = "app.database")
@Validated
public record DatabaseCredentials(
@NotBlank String username,
@NotBlank String password
) {}
@Bean
CommandLineRunner verifySecretBinding(DatabaseCredentials creds) {
return args -> {
// Sensitive values are validated at boot without logging plain text
log.info("Database credentials bound successfully for user: {}", creds.username());
log.info("Password length: {} characters (masked in telemetry)", creds.password().length());
};
}
}Synthesis & Production Verification
Production Acceptance Criteria
- Resolve one placeholder from an environment variable successfully
- Fail startup loudly when a required secret is absent
- Explain where your platform stores injected secrets
Key Takeaway
Production secrets must never be committed to source code or baked into container images; they must be injected via external secret engines and masked across all telemetry and logs.
Architectural & Technical Review
Core Interview Questions
Q01What is the Twelve-Factor App principle regarding secrets management?Show answer
Strict separation of config from code: credentials and secrets must be injected from the deployment environment (environment variables or secret volume mounts) and never stored in version control.
Q02How does Kubernetes configtree integration work with Spring Boot?Show answer
Spring Boot imports mounted secret directories via spring.config.import=configtree:/etc/secrets/, mapping each file in the volume directly to a property key in memory without exposing values in container environments.
Q03How does Spring Boot Actuator sanitize sensitive keys on /actuator/env?Show answer
Keys matching configured patterns (password, secret, key, token) are masked as ****** by default, and can be restricted to authorized roles via management.endpoint.env.show-values=when_authorized.
Production Pitfalls
Critical Anti-Patterns
- Pitfall: Using empty default fallbacks for required passwords — Using ${DB_PASSWORD:} supplies an empty string rather than failing fast at startup when the environment variable is missing.
- Pitfall: Exposing /actuator/env unauthenticated on public networks — Public exposure of environment endpoints leaks credentials and configurations, risking remote exploitation.
Module Certification Quiz
Prove Module Mastery
Comprehensive question set covering all lessons in this module.
Primary Documentation & Specifications
Lesson Primary References
Supplementary Module Resources