spring-native icon indicating copy to clipboard operation
spring-native copied to clipboard

Add Spring Cloud Config Vault support

Open RMostapha opened this issue 3 years ago • 6 comments
trafficstars

Hello everyone, I'm trying to run a POC for spring native project using:

  • graalVM (21.2.0.r11-grl)
  • spring boot (2.6.1)
  • spring cloud config (2021.0.0)
  • spring native (0.11.0) My app just expose two endpoint using restController , and retrieve config from Vault (using token authentication method). my pom.xml :
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.smarthome</groupId>
	<artifactId>enki-poc-v2-native</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>enki-poc-v2-native</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
		<repackage.classifier/>
		<spring-cloud.version>2021.0.0</spring-cloud.version>
		<spring-native.version>0.11.0</spring-native.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-vault-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.experimental</groupId>
			<artifactId>spring-native</artifactId>
			<version>${spring-native.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<classifier>${repackage.classifier}</classifier>
					<image>
						<builder>paketobuildpacks/builder:tiny</builder>
						<env>
							<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
						</env>
					</image>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.springframework.experimental</groupId>
				<artifactId>spring-aot-maven-plugin</artifactId>
				<version>${spring-native.version}</version>
				<executions>
					<execution>
						<id>test-generate</id>
						<goals>
							<goal>test-generate</goal>
						</goals>
					</execution>
					<execution>
						<id>generate</id>
						<goals>
							<goal>generate</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
	<repositories>
		<repository>
			<id>spring-releases</id>
			<name>Spring Releases</name>
			<url>https://repo.spring.io/release</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
	<pluginRepositories>
		<pluginRepository>
			<id>spring-releases</id>
			<name>Spring Releases</name>
			<url>https://repo.spring.io/release</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>

	<profiles>
		<profile>
			<id>native</id>
			<properties>
				<repackage.classifier>exec</repackage.classifier>
				<native-buildtools.version>0.9.8</native-buildtools.version>
			</properties>
			<dependencies>
				<dependency>
					<groupId>org.junit.platform</groupId>
					<artifactId>junit-platform-launcher</artifactId>
					<scope>test</scope>
				</dependency>
			</dependencies>
			<build>
				<plugins>
					<plugin>
						<groupId>org.graalvm.buildtools</groupId>
						<artifactId>native-maven-plugin</artifactId>
						<version>${native-buildtools.version}</version>
						<extensions>true</extensions>
						<executions>
							<execution>
								<id>test-native</id>
								<phase>test</phase>
								<goals>
									<goal>test</goal>
								</goals>
							</execution>
							<execution>
								<id>build-native</id>
								<phase>package</phase>
								<goals>
									<goal>build</goal>
								</goals>
							</execution>
						</executions>
					</plugin>
				</plugins>
			</build>
		</profile>
	</profiles>

</project>

my application.yml

spring:
  profiles:
    active: ${APP_PROFILES}

and my application-local.yml looks like :

spring:
  application.name: enki-poc-v2-native
  cloud.vault:
    application-name: ${spring.application.name}
    namespace: frlm/enki-foundations
    token: ${VAULT_TOKEN}
    uri: https://vault.factory.adeo.cloud
    authentication: token
    config.lifecycle.enabled: true
    fail-fast: true
  config:
    import: vault://secret/enki-poc-v2/local
  cloud:
    compatibility-verifier:
      enabled: false
# management api
management.endpoint.health.enabled: true
management.endpoints.web.base-path:

the build-native ( mvn spring-boot:build-image) run successfully , and I'm able to see the docker image created in my local repo.

When I try to run docker image using the command: docker run --rm -p 8080:8080 -e VAULT_TOKEN=s.uaJCGGWHG2Wpv3XptXG1HMba.4iZOA -e APP_PROFILES=local enki-poc-v2-native:0.0.1-SNAPSHOT

first I was having the issue : Native reflection configuration for org.springframework.cloud.vault.config.VaultConfigDataLocationResolver is missing. and I fix it by adding a TypeHint: @TypeHint(typeNames = "org.springframework.cloud.vault.config.VaultConfigDataLocationResolver")

Now I'm facing another issue , with the error:

➜  enki-poc-v2-native git:(main) ✗ docker run --rm -p 8080:8080 -e VAULT_TOKEN=s.uaJCGGWHG2Wpv3XptXG1HMba.4iZOA -e APP_PROFILES=local enki-poc-v2-native:0.0.1-SNAPSHOT
15:25:51.372 [main] INFO org.springframework.boot.SpringApplication - AOT mode enabled
15:25:51.416 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.IllegalArgumentException: Unable to instantiate org.springframework.boot.context.config.ConfigDataLoader [org.springframework.cloud.vault.config.VaultConfigDataLoader]
        at org.springframework.boot.util.Instantiator.instantiate(Instantiator.java:131)
        at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
        at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
        at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
        at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
        at org.springframework.boot.util.Instantiator.instantiate(Instantiator.java:118)
        at org.springframework.boot.util.Instantiator.instantiate(Instantiator.java:103)
        at org.springframework.boot.context.config.ConfigDataLoaders.<init>(ConfigDataLoaders.java:80)
        at org.springframework.boot.context.config.ConfigDataLoaders.<init>(ConfigDataLoaders.java:58)
        at org.springframework.boot.context.config.ConfigDataEnvironment.<init>(ConfigDataEnvironment.java:152)
        at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.getConfigDataEnvironment(ConfigDataEnvironmentPostProcessor.java:114)
        at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:102)
        at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:94)
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:102)
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:87)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
        at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:85)
        at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:66)
        at java.util.ArrayList.forEach(ArrayList.java:1541)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:120)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:114)
        at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:65)
        at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:338)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:296)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290)
        at com.smarthome.enkipocv2native.EnkiPocV2NativeApplication.main(EnkiPocV2NativeApplication.java:13)
Caused by: java.lang.reflect.InvocationTargetException: null
        at java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at org.springframework.boot.util.Instantiator.instantiate(Instantiator.java:143)
        at org.springframework.boot.util.Instantiator.instantiate(Instantiator.java:127)
        ... 33 common frames omitted
Caused by: java.lang.IllegalArgumentException: Class must not be null
        at org.springframework.util.Assert.notNull(Assert.java:201)
        at org.springframework.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:730)
        at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:704)
        at org.springframework.cloud.vault.config.VaultConfigDataLoader.reconfigureLogger(VaultConfigDataLoader.java:388)
        at org.springframework.cloud.vault.config.VaultConfigDataLoader.lambda$reconfigureLoggers$18(VaultConfigDataLoader.java:383)
        at java.util.Arrays$ArrayList.forEach(Arrays.java:4390)
        at org.springframework.cloud.vault.config.VaultConfigDataLoader.reconfigureLoggers(VaultConfigDataLoader.java:383)
        at org.springframework.cloud.vault.config.VaultConfigDataLoader.<init>(VaultConfigDataLoader.java:109)
        ... 36 common frames omitted

Any ideas or suggestions to how I can debug it further or get past this? Many thanks PS: I don't have any issue when running my jar file directly

RMostapha avatar Dec 10 '21 15:12 RMostapha

I was able to solve this by adding the following native hint to my project:

@NativeHint(
    types = @TypeHint(
        types = {
            VaultConfigDataLocationResolver.class
        },
        typeNames = {
            // These two taken from org.springframework.cloud.vault.config.VaultConfigDataLoader#reconfigureLoggers
            "org.springframework.vault.core.lease.SecretLeaseContainer$LeaseRenewalScheduler",
            "org.springframework.vault.core.lease.SecretLeaseEventPublisher$LoggingErrorListener"
        }
    )
)

nbruno avatar Dec 21 '21 19:12 nbruno

Thank you @nbruno I tried your solution , Now the application start , but it's not able to retrieve the vault token , I'm having this error:

org.springframework.vault.VaultException: Cannot initialize PropertySource for secret at secret/enki-poc-v2/local; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.springframework.vault.support.VaultResponse]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.vault.support.VaultResponse` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

I tried adding a the class VaultResponse to the TypeHint , but I'm still having other issues

Recreating enki-poc-v2-native_poc-service_1 ... done
Attaching to enki-poc-v2-native_poc-service_1
poc-service_1  | 13:07:37.805 [main] INFO org.springframework.boot.SpringApplication - AOT mode enabled
poc-service_1  | 13:07:38.503 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
poc-service_1  | org.springframework.vault.VaultException: Cannot initialize PropertySource for secret at secret/enki-poc-v2/local; nested exception is java.lang.IllegalStateException: Token response is null
poc-service_1  |        at org.springframework.cloud.vault.config.VaultConfigDataLoader.createLeasingPropertySourceFailFast(VaultConfigDataLoader.java:321)
poc-service_1  |        at org.springframework.cloud.vault.config.VaultConfigDataLoader.loadConfigData(VaultConfigDataLoader.java:159)
poc-service_1  |        at org.springframework.cloud.vault.config.VaultConfigDataLoader.load(VaultConfigDataLoader.java:140)
poc-service_1  |        at org.springframework.cloud.vault.config.VaultConfigDataLoader.load(VaultConfigDataLoader.java:93)
poc-service_1  |        at org.springframework.boot.context.config.ConfigDataLoaders.load(ConfigDataLoaders.java:107)
poc-service_1  |        at org.springframework.boot.context.config.ConfigDataImporter.load(ConfigDataImporter.java:128)
poc-service_1  |        at org.springframework.boot.context.config.ConfigDataImporter.resolveAndLoad(ConfigDataImporter.java:86)
poc-service_1  |        at org.springframework.boot.context.config.ConfigDataEnvironmentContributors.withProcessedImports(ConfigDataEnvironmentContributors.java:121)
poc-service_1  |        at org.springframework.boot.context.config.ConfigDataEnvironment.processWithProfiles(ConfigDataEnvironment.java:311)
poc-service_1  |        at org.springframework.boot.context.config.ConfigDataEnvironment.processAndApply(ConfigDataEnvironment.java:232)
poc-service_1  |        at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:102)
poc-service_1  |        at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:94)
poc-service_1  |        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:102)
poc-service_1  |        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:87)
poc-service_1  |        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
poc-service_1  |        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
poc-service_1  |        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
poc-service_1  |        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
poc-service_1  |        at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:85)
poc-service_1  |        at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:66)
poc-service_1  |        at java.util.ArrayList.forEach(ArrayList.java:1541)
poc-service_1  |        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:120)
poc-service_1  |        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:114)
poc-service_1  |        at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:65)
poc-service_1  |        at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:338)
poc-service_1  |        at org.springframework.boot.SpringApplication.run(SpringApplication.java:296)
poc-service_1  |        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)
poc-service_1  |        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290)
poc-service_1  |        at com.smarthome.enkipocv2native.EnkiPocV2NativeApplication.main(EnkiPocV2NativeApplication.java:29)
poc-service_1  | Caused by: java.lang.IllegalStateException: Token response is null
poc-service_1  |        at org.springframework.util.Assert.state(Assert.java:76)
poc-service_1  |        at org.springframework.vault.authentication.LoginTokenAdapter.lookupSelf(LoginTokenAdapter.java:96)
poc-service_1  |        at org.springframework.vault.authentication.LoginTokenAdapter.augmentWithSelfLookup(LoginTokenAdapter.java:78)
poc-service_1  |        at org.springframework.vault.authentication.LifecycleAwareSessionManager.doGetSessionToken(LifecycleAwareSessionManager.java:291)
poc-service_1  |        at org.springframework.vault.authentication.LifecycleAwareSessionManager.getSessionToken(LifecycleAwareSessionManager.java:266)
poc-service_1  |        at org.springframework.vault.core.VaultTemplate.lambda$getSessionInterceptor$1(VaultTemplate.java:253)
poc-service_1  |        at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93)
poc-service_1  |        at org.springframework.vault.client.RestTemplateBuilder.lambda$createTemplate$4(RestTemplateBuilder.java:239)
poc-service_1  |        at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93)
poc-service_1  |        at org.springframework.vault.client.VaultClients.lambda$createRestTemplate$0(VaultClients.java:122)
poc-service_1  |        at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93)
poc-service_1  |        at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:77)
poc-service_1  |        at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
poc-service_1  |        at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
poc-service_1  |        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:776)
poc-service_1  |        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711)
poc-service_1  |        at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:334)
poc-service_1  |        at org.springframework.vault.core.VaultTemplate.lambda$doRead$5(VaultTemplate.java:461)
poc-service_1  |        at org.springframework.vault.core.VaultTemplate.doWithSession(VaultTemplate.java:448)
poc-service_1  |        at org.springframework.vault.core.VaultTemplate.doRead(VaultTemplate.java:458)
poc-service_1  |        at org.springframework.vault.core.VaultTemplate.read(VaultTemplate.java:353)
poc-service_1  |        at org.springframework.vault.core.lease.SecretLeaseContainer.doGetSecrets(SecretLeaseContainer.java:645)
poc-service_1  |        at org.springframework.vault.core.lease.SecretLeaseContainer.doStart(SecretLeaseContainer.java:390)
poc-service_1  |        at org.springframework.vault.core.lease.SecretLeaseContainer.start(SecretLeaseContainer.java:380)
poc-service_1  |        at org.springframework.vault.core.lease.SecretLeaseContainer.addRequestedSecret(SecretLeaseContainer.java:343)
poc-service_1  |        at org.springframework.vault.core.env.LeaseAwareVaultPropertySource.loadProperties(LeaseAwareVaultPropertySource.java:176)
poc-service_1  |        at org.springframework.vault.core.env.LeaseAwareVaultPropertySource.<init>(LeaseAwareVaultPropertySource.java:161)
poc-service_1  |        at org.springframework.vault.core.env.LeaseAwareVaultPropertySource.<init>(LeaseAwareVaultPropertySource.java:119)
poc-service_1  |        at org.springframework.cloud.vault.config.VaultConfigDataLoader.createLeasingPropertySource(VaultConfigDataLoader.java:287)
poc-service_1  |        at org.springframework.cloud.vault.config.VaultConfigDataLoader.createLeasingPropertySourceFailFast(VaultConfigDataLoader.java:310)
poc-service_1  |        ... 28 common frames omitted
enki-poc-v2-native_poc-service_1 exited with code 1

I already checked the VaultToken it's Valid , when it's not I'm getting 403 . Maybe my configuration of TypeHint it's not good , I'm new to this, I think Spring native still don't know my class "VaultResponse" Any Idea ?

RMostapha avatar Dec 22 '21 16:12 RMostapha

@OlgaMaciaszek Could you please check if we should added related hints?

sdeleuze avatar Jan 03 '22 10:01 sdeleuze

See another related error reported via #1423 that I closed as a duplicate.

sdeleuze avatar Jan 04 '22 14:01 sdeleuze

Hi, what about progress?

SimSonic avatar Apr 10 '22 10:04 SimSonic

Backlog milestone means we ans we are not actively working on this, and that we will re evaluate fixing that in Spring Boot 3 timeframe.

sdeleuze avatar Apr 13 '22 07:04 sdeleuze

Spring Native is now superseded by Spring Boot 3 official native support, see the related reference documentation for more details.

As a consequence, I am closing this issue, and recommend trying your use case with latest Spring Boot 3 version. If you still experience the issue reported here, please open an issue directly on the related Spring project (Spring Framework, Data, Security, Boot, Cloud, etc.) with a reproducer.

Thanks for your contribution on the experimental Spring Native project, we hope you will enjoy the official native support introduced by Spring Boot 3.

sdeleuze avatar Jan 02 '23 12:01 sdeleuze