I have the following Spring Boot (3.3.0) config.yaml:
spring:
datasource:
appname: ${DOCUMENT_ID_DATABASE_APPNAME:DocumentIdService}
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql:characterEncoding=UTF8&rewriteBatchedStatements=true
username: ${DOCUMENT_ID_DATABASE_USERNAME:postgres}
password: ${DOCUMENT_ID_DATABASE_PASSWORD:password}
Instead of password being read from the DOCUMENT_ID_DATABASE_PASSWORD env var, I would instead like the password to be read by the file pointed to by the env var, for example if DOCUMENT_ID_DATABASE_PASSWORD_FILE=/tmp/passwordfile
, then that would mean:
password: // Set to content of /tmp/passwordfile, or if /tmp/passwordfile does not exist, default to "mypassword"
The full solution is:
import java.io.IOException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
public final class SecretPropertyResolver implements EnvironmentPostProcessor
{
private static final String SECRET_PROPERTY_PREFIX = "secret.";
@Override
public void postProcessEnvironment(final ConfigurableEnvironment environment, final SpringApplication application)
{
environment.getPropertySources().addFirst(new PropertySource<Object>("SecretPropertyResolver")
{
@Override
public Object getProperty(String name)
{
// Assuming:
// ${secret.MY_PASSWORD:password}
// This will get called twice:
// Once with: secret.MY_PASSWORD:password
// Once with: secret.MY_PASSWORD
// So we need to check for absence of the colon before continuing
if (name.startsWith(SECRET_PROPERTY_PREFIX) && !name.contains(":")) {
final String key = name.substring(SECRET_PROPERTY_PREFIX.length());
try {
// Logic to read from env/file here
} catch (final IOException e) {
throw new RuntimeException(String.format("Unable to read secret '%s'", key), e);
}
}
return null;
}
});
}
}
src/main/resources/META-INF/spring-factories
org.springframework.boot.env.EnvironmentPostProcessor=com.example.SecretPropertyResolver
application.yaml
password: ${secret.MY_PASSWORD:password}