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

Comment From: ryanjbaxter

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

Comment From: patpatpat123

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.

Comment From: 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

Comment From: 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

Comment From: ryanjbaxter

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

Comment From: patpatpat123

Uploading spring-cloud-configserver-vault.zip…

Comment From: 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.