spring-cloud-config icon indicating copy to clipboard operation
spring-cloud-config copied to clipboard

Spring 2.4.2 + llford : spring.cloud.config.server.vault.authentication=cert not taken into account

Open patpatpat123 opened this issue 4 years ago • 7 comments

Hello Spring Cloud Config Server Team,

We have a Spring Cloud Config Server via Spring Boot 2.4.2 + Ilford. The back end is Hashicorp Vault. We are using only the config server jar, no Spring-Vault-Config, no BetterCloud, etc...

Our current setup is:

spring.cloud.config.server.vault.backend=cubbyhole/somepath
spring.cloud.config.server.vault.host=our-vault-instance
spring.cloud.config.server.vault.port=443
spring.cloud.config.server.vault.scheme=https
spring.cloud.config.server.vault.authentication=cert
spring.cloud.config.server.vault.ssl.key-store=file://path/to/keystore.p12
spring.cloud.config.server.vault.ssl.key-store-password=the-password
spring.cloud.config.server.vault.ssl.cert-auth-path=cert
#spring.cloud.config.server.vault.token=we are NOT using token

And on client side, very straightforward

spring.config.import=optional:configserver:https://the-config-server-with-vault-back-end:8080

When the client starts, we see this in spring cloud config server debug logs:

o.s.web.servlet.DispatcherServlet        : GET "/the-config-server-route/vault-path", parameters={}
.m.m.a.ExceptionHandlerExceptionResolver : Resolved [java.lang.IllegalArgumentException: Missing required header in HttpServletRequest: X-Config-Token]
o.s.web.servlet.DispatcherServlet        : Completed 400 BAD_REQUEST
s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
o.s.security.web.FilterChainProxy        : Securing GET /error
s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
o.s.security.web.FilterChainProxy        : Secured GET /error
o.s.web.servlet.DispatcherServlet        : "ERROR" dispatch for GET "/error", parameters={}
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Using 'application/vnd.spring-cloud.config-server.v2+json', given [application/vnd.spring-cloud.config-server.v2+json] and supported [application/json, application/*+json, application/json, application/*+json]
w.c.HttpSessionSecurityContextRepository : Did not store anonymous SecurityContext
o.s.web.servlet.DispatcherServlet        : Exiting from "ERROR" dispatch, status 400

As you can see, it is still expecting a token as if we were not trying to connect via cert, but via token.

Te expected behavior would have been a connection to HTTP POST https://vault:443/v1/auth/cert/login in order to fetch the token first.

However, it is currently not sending any request to v1/auth/cert/login (Vault log monitored) and directly failing with the MISSING TOKEN.

Thank you

patpatpat123 avatar Feb 04 '21 22:02 patpatpat123

I assume this worked in Hoxton.SR9? Can you provide a sample?

ryanjbaxter avatar Feb 05 '21 00:02 ryanjbaxter

Hello @ryanjbaxter ,

Correct, it was working fine before. Will try to extract an example. But maybe without the vault server itself, it might not help reproduce, since the server is just a ATConfigServer, and the client just point to the server, no code in the middle.

Will update this asap.

patpatpat123 avatar Feb 08 '21 14:02 patpatpat123

Hello @ryanjbaxter

Actually, the setup is pretty straightforward. Since I cannot provide an entire secure Vault with cert auth enabled, it is just 6 files in total (1 java file + 1 property file + 1 pom file), times two, client and server.

Server

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.marcosbarbero.wd</groupId>
	<artifactId>config-server</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>config-server</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>11</java.version>
		<spring-cloud.version>2020.0.0</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</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>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>

</project>

package com.marcosbarbero.wd.configserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

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

logging.level.ROOT=DEBUG
management.endpoints.web.exposure.include=info, health, env
server.port=8090
spring.profiles.active=vault
spring.application.name=configserver


spring.cloud.config.server.prefix=/config
spring.cloud.config.server.vault.backend=cubbyhole/test
spring.cloud.config.server.vault.host=the-vault-server-with-cert-auth
spring.cloud.config.server.vault.kvVersion=1
spring.cloud.config.server.vault.port=443
spring.cloud.config.server.vault.scheme=https
spring.cloud.config.server.vault.authentication=cert
spring.cloud.config.server.vault.ssl.key-store=file:/path/to/keystore/keystore-to-connect-to-vault.p12
spring.cloud.config.server.vault.ssl.key-store-password=
spring.cloud.config.server.vault.ssl.trust-store=file:/path/to/truststore/i-do-trust-vault.p12
spring.cloud.config.server.vault.ssl.trust-store-password=

#Note, this is using cert auth, not token

patpatpat123 avatar Feb 11 '21 16:02 patpatpat123

Client

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.marcosbarbero.wd</groupId>
	<artifactId>config-client</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>config-client</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>11</java.version>
		<spring-cloud.version>2020.0.0</spring-cloud.version>
	</properties>

	<dependencies>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-client</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</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>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>


</project>

package com.marcosbarbero.wd.configclient;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Configuration
@RestController
@RefreshScope
@SpringBootApplication
public class ConfigClientApplication {

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

    @GetMapping("/property")
    public ResponseEntity<String> getProperty() {
        return someService();
    }

    @Value("${client.pseudo.property}")
    private String pseudoProperty;

    public String getPseudoProperty() {
        return pseudoProperty;
    }

    public ResponseEntity<String> someService() {
        return ResponseEntity.ok(getPseudoProperty());
    }


}

logging.level.ROOT=debug
spring.application.name=configclient
# spring.cloud.config.uri=http://localhost:8090/config not used anymore since 2.4
spring.config.import=optional:configserver:https://localhost:8090/config
spring.profiles.active=vault

patpatpat123 avatar Feb 11 '21 16:02 patpatpat123

Could you put the code in a git repo or attach it as a zip?

ryanjbaxter avatar Feb 11 '21 16:02 ryanjbaxter

Uploading spring-cloud-configserver-vault.zip…

patpatpat123 avatar Feb 11 '21 16:02 patpatpat123

And running it will yield the

.m.m.a.ExceptionHandlerExceptionResolver : Resolved [java.lang.IllegalArgumentException: Missing required header in HttpServletRequest: X-Config-Token]
o.s.web.servlet.DispatcherServlet        : Completed 400 BAD_REQUEST

Again, apologies I cannot provide a secure vault with certificates configured, alongside the client certificates.

patpatpat123 avatar Feb 11 '21 16:02 patpatpat123