javaspring-boothashicorp-vaultspring-cloud-vault-config

Spring Cloud Vault Config Databases Doesn't load the properties without bootstrap properties


Describe the bug We are using database secret engine in hashicorp vault with dynamic role setup to get dynamic credentials. We are trying to fetch the same in Springboot application using spring-cloud-vault-config-databases, however the properties are not working until we enable and specify the bootstrap yaml properties. They should have working as is with application properties but they don't work.

Sample pom.xml

4.0.0 org.springframework.boot spring-boot-starter-parent 2.7.8 com.fyndna vault-demo 0.0.1-SNAPSHOT vault-demo Demo project for integration with Hashicorp Vault 17 2021.0.2 org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-bootstrap org.springframework.boot spring-boot-starter-data-jpa org.springframework.cloud spring-cloud-vault-config-databases org.postgresql postgresql runtime org.springframework.boot spring-boot-starter-validation
    <!-- Dapr's core SDK with all features, except Actors. -->
    <dependency>
        <groupId>io.dapr</groupId>
        <artifactId>dapr-sdk</artifactId>
        <version>1.7.1</version>
    </dependency>
    <!-- Dapr's SDK for Actors (optional). -->
    <dependency>
        <groupId>io.dapr</groupId>
        <artifactId>dapr-sdk-actors</artifactId>
        <version>1.7.1</version>
    </dependency>
    <!-- Dapr's SDK integration with SpringBoot (optional). -->
    <dependency>
        <groupId>io.dapr</groupId>
        <artifactId>dapr-sdk-springboot</artifactId>
        <version>1.7.1</version>
    </dependency>

    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.9.0</version>
    </dependency>

    <dependency>
        <groupId>net.sourceforge.tess4j</groupId>
        <artifactId>tess4j</artifactId>
        <version>2.0.1</version>
    </dependency>

    <dependency>
        <groupId>org.openpnp</groupId>
        <artifactId>opencv</artifactId>
        <version>[4.3.0,)</version>
    </dependency>


    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>4.1.2</version>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.12.0</version>
    </dependency>

    <dependency>
        <groupId>com.jayway.jsonpath</groupId>
        <artifactId>json-path</artifactId>
        <version>2.4.0</version>
    </dependency>

    <dependency>
        <groupId>jakarta.validation</groupId>
        <artifactId>jakarta.validation-api</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
bootstrap yaml ---
spring:
application:
name: vault-demo
cloud:
vault:
authentication: TOKEN
token: 00000000-0000-0000-0000-000000000000
host: localhost
port: 8200
scheme: http
fail-fast: true
config:
lifecycle:
enabled: true
min-renewal: 5s
expiry-threshold: 1m
generic:
enabled: true
backend: secret
database:
enabled: true
role: dynamic-role
backend: database
datasource:
url: jdbc:postgresql://localhost:5433/yugabyte
platform: postgres
type: com.zaxxer.hikari.HikariDataSource
hikari:
transactionIsolation: TRANSACTION_SERIALIZABLE
schema: alert_manager
jpa:
show-sql: true
generate-ddl: true
hibernate:
ddl-auto: validate

package com.mycomp.security.vault;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.vault.authentication.SessionManager;

@SpringBootApplication
@EnableJpaAuditing
public class VaultDemoApplication {

private static final Logger logger = LoggerFactory.getLogger(VaultDemoApplication.class);
@Autowired
private SessionManager sessionManager;

@Value("${spring.datasource.username}")
private String dbUser;

@Value("${spring.datasource.password}")
private String dbPass;

public static void main(String[] args) {
    SpringApplication.run(VaultDemoApplication.class, args);
}

@PostConstruct
public void initIt() throws Exception {
    logger.info("Got Vault Token: " + sessionManager.getSessionToken().getToken());
    logger.info("Got DB User: " + dbUser);
    logger.info("Got DB Pass: " + dbPass);
}
}

---- Now if we disable the bootstrap and rename the bootstrap property with application yaml then it doesn't work and thorws errors and stacktraces for bootstrap properties ---- please see the below logs ----

-------------------------------======================================================--------------------------

. ____ _ __ _ _
/\ / ' __ _ () __ __ _ \ \ \
( ( )__ | '_ | '| | ' / ` | \ \ \
\/ )| |)| | | | | || (| | ) ) ) )
' || .__|| ||| |_, | / / / /
=========||==============|/=////
:: Spring Boot :: (v2.7.8)

2023-08-02 11:14:05.806 WARN 83143 --- [ main] o.s.v.a.LifecycleAwareSessionManager : Cannot enhance VaultToken to a LoginToken: Token self-lookup failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:8200/v1/auth/token/lookup-self": Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8200; nested exception is java.net.ConnectException: Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8200
2023-08-02 11:14:05.865 INFO 83143 --- [ main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-secret/application'}]
2023-08-02 11:14:05.894 INFO 83143 --- [ main] c.f.security.vault.VaultDemoApplication : No active profile set, falling back to 1 default profile: "default"
2023-08-02 11:14:05.974 WARN 83143 --- [ main] LeaseEventPublisher$LoggingErrorListener : [RequestedSecret [path='secret/application', mode=ROTATE]] Lease [leaseId='null', leaseDuration=PT0S, renewable=false] I/O error on GET request for "https://localhost:8200/v1/secret/application": Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8200; nested exception is java.net.ConnectException: Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8200

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:8200/v1/secret/application": Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8200; nested exception is java.net.ConnectException: Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8200
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:791) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:717) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:340) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.vault.core.VaultTemplate.lambda$doRead$5(VaultTemplate.java:461) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.vault.core.VaultTemplate.doWithSession(VaultTemplate.java:448) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.vault.core.VaultTemplate.doRead(VaultTemplate.java:458) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.vault.core.VaultTemplate.read(VaultTemplate.java:353) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.vault.core.lease.SecretLeaseContainer.doGetSecrets(SecretLeaseContainer.java:645) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.vault.core.lease.SecretLeaseContainer.doStart(SecretLeaseContainer.java:390) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.vault.core.lease.SecretLeaseContainer.start(SecretLeaseContainer.java:380) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.vault.core.lease.SecretLeaseContainer.addRequestedSecret(SecretLeaseContainer.java:343) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.vault.core.env.LeaseAwareVaultPropertySource.loadProperties(LeaseAwareVaultPropertySource.java:176) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.vault.core.env.LeaseAwareVaultPropertySource.(LeaseAwareVaultPropertySource.java:161) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.vault.core.env.LeaseAwareVaultPropertySource.(LeaseAwareVaultPropertySource.java:119) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.cloud.vault.config.LeasingVaultPropertySourceLocator.createVaultPropertySource(LeasingVaultPropertySourceLocator.java:146) ~[spring-cloud-vault-config-3.1.0.jar:3.1.0]
at org.springframework.cloud.vault.config.LeasingVaultPropertySourceLocator.createVaultPropertySource(LeasingVaultPropertySourceLocator.java:83) ~[spring-cloud-vault-config-3.1.0.jar:3.1.0]
at org.springframework.cloud.vault.config.VaultPropertySourceLocatorSupport.doCreatePropertySources(VaultPropertySourceLocatorSupport.java:122) ~[spring-cloud-vault-config-3.1.0.jar:3.1.0]
at org.springframework.cloud.vault.config.VaultPropertySourceLocatorSupport.createCompositePropertySource(VaultPropertySourceLocatorSupport.java:101) ~[spring-cloud-vault-config-3.1.0.jar:3.1.0]
at org.springframework.cloud.vault.config.VaultPropertySourceLocatorSupport.locate(VaultPropertySourceLocatorSupport.java:76) ~[spring-cloud-vault-config-3.1.0.jar:3.1.0]
at org.springframework.cloud.bootstrap.config.PropertySourceLocator.locateCollection(PropertySourceLocator.java:51) ~[spring-cloud-context-3.1.2.jar:3.1.2]
at org.springframework.cloud.bootstrap.config.PropertySourceLocator.locateCollection(PropertySourceLocator.java:47) ~[spring-cloud-context-3.1.2.jar:3.1.2]
at org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration.initialize(PropertySourceBootstrapConfiguration.java:95) ~[spring-cloud-context-3.1.2.jar:3.1.2]
at org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:604) ~[spring-boot-2.7.8.jar:2.7.8]
at org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:373) ~[spring-boot-2.7.8.jar:2.7.8]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:306) ~[spring-boot-2.7.8.jar:2.7.8]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.8.jar:2.7.8]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.8.jar:2.7.8]
at com.mycomp.security.vault.VaultDemoApplication.main(VaultDemoApplication.java:29) ~[classes/:na]
Caused by: java.net.ConnectException: Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8200
at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.kt:297) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:207) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154) ~[okhttp-4.9.0.jar:na]
at org.springframework.http.client.OkHttp3ClientHttpRequest.executeInternal(OkHttp3ClientHttpRequest.java:73) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:109) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.vault.core.VaultTemplate.lambda$getSessionInterceptor$1(VaultTemplate.java:255) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.vault.client.RestTemplateBuilder.lambda$createTemplate$4(RestTemplateBuilder.java:239) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.vault.client.VaultClients.lambda$createRestTemplate$0(VaultClients.java:122) ~[spring-vault-core-2.3.2.jar:2.3.2]
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:77) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) ~[spring-web-5.3.25.jar:5.3.25]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:782) ~[spring-web-5.3.25.jar:5.3.25]
... 27 common frames omitted
Suppressed: javax.net.ssl.SSLException: Unsupported or unrecognized SSL message
at java.base/sun.security.ssl.SSLSocketInputRecord.handleUnknownRecord(SSLSocketInputRecord.java:451) ~[na:na]
at java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:175) ~[na:na]
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:111) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1500) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1415) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:450) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:421) ~[na:na]
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:379) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209) ~[okhttp-4.9.0.jar:na]
... 55 common frames omitted
Caused by: java.net.ConnectException: Connection refused
at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:549) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:597) ~[na:na]
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) ~[na:na]
at java.base/java.net.Socket.connect(Socket.java:633) ~[na:na]
at okhttp3.internal.platform.Platform.connectSocket(Platform.kt:120) ~[okhttp-4.9.0.jar:na]
at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.kt:295) ~[okhttp-4.9.0.jar:na]
... 56 common frames omitted

2023-08-02 11:14:05.977 INFO 83143 --- [ main] o.s.v.c.e.LeaseAwareVaultPropertySource : Vault location [secret/application] not resolvable: I/O error on GET request for "https://localhost:8200/v1/secret/application": Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8200; nested exception is java.net.ConnectException: Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8200
2023-08-02 11:14:06.845 INFO 83143 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-08-02 11:14:06.965 INFO 83143 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 114 ms. Found 1 JPA repository interfaces.
2023-08-02 11:14:07.165 INFO 83143 --- [ main] o.s.cloud.context.scope.GenericScope : BeanFactory id=e2ef9e1a-5270-34d9-869d-92664cf987c9
2023-08-02 11:14:07.647 INFO 83143 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-08-02 11:14:07.656 INFO 83143 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-08-02 11:14:07.657 INFO 83143 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.71]
2023-08-02 11:14:07.811 INFO 83143 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-08-02 11:14:07.812 INFO 83143 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1832 ms
2023-08-02 11:14:07.990 INFO 83143 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-08-02 11:14:08.026 INFO 83143 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.6.14.Final
2023-08-02 11:14:08.230 INFO 83143 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2023-08-02 11:14:08.362 INFO 83143 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-08-02 11:14:09.677 ERROR 83143 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Exception during pool initialization.

org.postgresql.util.PSQLException: FATAL: role "vithakur" does not exist
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2675) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.core.v3.QueryExecutorImpl.readStartupMessages(QueryExecutorImpl.java:2787) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.core.v3.QueryExecutorImpl.(QueryExecutorImpl.java:173) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:290) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.jdbc.PgConnection.(PgConnection.java:223) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.Driver.makeConnection(Driver.java:402) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.Driver.connect(Driver.java:261) ~[postgresql-42.3.8.jar:42.3.8]
at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:121) ~[HikariCP-4.0.3.jar:na]
at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:364) ~[HikariCP-4.0.3.jar:na]
at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:206) ~[HikariCP-4.0.3.jar:na]

Solution

  • I have solved this problem by removing the bootstrap starter dependency, moving the bootstrap properties to application yaml, and including config for vault in application yaml

    config: import: optional:vault://

    Please refer to the documentation for this below,

    https://docs.spring.io/spring-cloud-vault/docs/current/reference/html/#vault.configdata.location.optional