Custom property loader with Spring Cloud Config

2.3k views Asked by At

I'm using Spring Cloud Config in my spring-boot application and I need to write some custom code to handle properties to be read from my corporate password vault when property is flagged as such. I know spring cloud supports Hashicorp Vault, but that's not the one in case.

I don't want to hard-code specific properties to be retrieved from a different source, for example, I would have a properties file for application app1 with profile dev with values:

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

but for some other profiles such as prod, I would have:

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=prod-user
spring.datasource.password=[[vault]]
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

So I need the custom property vault to intercept the property loaded whenever it finds a returned value equals to [[vault]] (or some other type of flag), and query from the corporate vault instead. In this case, my custom property loader would find the value of property spring.datasource.password from the corporate password vault. All other properties would still be returned as-is from values loaded by standard spring cloud config client.

I would like to do that using annotated code only, no XML configuration.

2

There are 2 answers

0
Aleksandar Radulović On

While trying to solve the identical problem, I believe that I have come to work-around that may be acceptable.

Here is my solution below.

public class JBossVaultEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
    MutablePropertySources propertySources = environment.getPropertySources();
    Map<String, String> sensitiveProperties = propertySources.stream()
            .filter(propertySource -> propertySource instanceof EnumerablePropertySource)
            .map(propertySource -> (EnumerablePropertySource<?>) propertySource)
            .map(propertySource -> {
                Map<String, String> vaultProperties = new HashMap<>();
                String[] propertyNames = propertySource.getPropertyNames();
                for (String propertyName : propertyNames) {
                    String propertyValue = propertySource.getProperty(propertyName).toString();
                    if (propertyValue.startsWith("VAULT::")) {
                        vaultProperties.put(propertyName, propertyValue);
                    }
                }
                return vaultProperties;
            })
            .reduce(new HashMap<>(), (m1, m2) -> {
                m1.putAll(m2);
                return m1;
            });

    Map<String, Object> vaultProperties = new HashMap<>();
    sensitiveProperties.keySet().stream()
            .forEach(key -> {
                vaultProperties.put(key, VaultReader.readAttributeValue(sensitiveProperties.get(key)));
            });

    propertySources.addFirst(new MapPropertySource("vaultProperties", vaultProperties));
}
0
Aleksander Wolek On

You can implement your own PropertySourceLocator and add entry to spring.factories in directory META-INF.

#spring.factories

org.springframework.cloud.bootstrap.BootstrapConfiguration=/
foo.bar.MyPropertySourceLocator

Then you can you can refer to keys in your corporate password vault like a normal properties in spring.

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=prod-user
spring.datasource.password=${lodaded.password.from.corporate.vault}
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

Implementation by HasiCorp: VaultPropertySourceLocatorSupport