'Spring Cloud Config with SSL handshake
I'm trying to authenticate my Spring Boot application (Client) to my Spring Cloud Config Server. This works well with basic username/password using Spring Security, but I want to use SSL with an x509 certificate.
I understand that for this you need to bootstrap your own custom RestTemplate with your own custom HttpComponentsClientHttpRequestFactory in your Client that overwrites the default behaviour and teaches your Client how to contact the Server using its own truststore.jks (https://piotrminkowski.com/2019/12/03/secure-spring-cloud-config/).
To be able to force the bootstrap I understand that we need to add a spring.factories file under META_INF with something like:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.andrelcode.myspringcloudconfigclient.config.SSLConfigServiceBootstrapConfiguration
(Note: Spring clearly recognizes my spring.factories file, as it automatically assigns a "spring factories registration gutter icon to it)
Of course, it's not the end of the Story as we also need to specifically enable bootstrap since spring-cloud.version 2020.0:
Processing of Bootstrap configuration is disabled by default in Spring Cloud 2020.0 and later. To re-enable it, set spring.cloud.bootstrap.enabled=true (from: Spring Boot application does not load spring.factories)
So I did this as well, by adding a bootstrap.properties file to my project, with the given line:
spring.cloud.bootstrap.enabled=true
So at this point I have the followings:
src |main | |java | | |com.andrelcode.myspringcloudconfigclient | | | |config | | | | |SSLConfigServiceBootstrapConfiguration() | | |MyspringcloudconfigclientApplication() -> @SpringBootApplication, @EnableWebMvc |resources | |META_INF | | |spring.factories -> org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.andrelcode.myspringcloudconfigclient.config.SSLConfigServiceBootstrapConfiguration | |application.yml | |bootstrap.properties -> spring.cloud.bootstrap.enabled=true
And yet, when I launch the application it clearly ignores all my bootstrap config and simply fails to start because it cannot pull its configuration from the Server:
Caused by: org.springframework.web.client.HttpClientErrorException$BadRequest: 400 : "Bad RequestThis combination of host and port requires TLS."
In the Server log I see:
java.io.IOException: Found an plain text HTTP request on what should be an encrypted TLS connection
So again, it seems that Spring Boot in my Client completely ignores my efforts to bootstrap my own RestTemplate using my custom HttpComponentsClientHttpRequestFactory that it should use to talk to the Config Server. I'm not sure how DEBUG this any further, as I don't get any additional logs even if I set TRACE.
Here is the implementation of the Configuration I'd like to bootstrap to my Client (although it's the same as in the example link from Piotr above):
package com.andrelcode.myspringcloudconfigclient.config;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.config.client.ConfigClientProperties;
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
@Configuration
public class SSLConfigServiceBootstrapConfiguration {
@Autowired
ConfigClientProperties configClientProperties;
@Bean
public ConfigServicePropertySourceLocator configServerPropertySourceLocator() throws Exception {
final char[] password = "testtest".toCharArray();
final FileSystemResource resource = new FileSystemResource("C:\\******truststore.jks");
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(resource.getFile(), password, password)
.loadTrustMaterial(resource.getFile(), password, new TrustSelfSignedStrategy()).build();
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier((s, sslSession) -> true)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
ConfigServicePropertySourceLocator configServicePropertySourceLocator = new ConfigServicePropertySourceLocator(configClientProperties);
configServicePropertySourceLocator.setRestTemplate(new RestTemplate(requestFactory));
return configServicePropertySourceLocator;
}
}
So what's missing or what's preventing the properly bootstrapping my custom configuration at startup in my Client?
Thanks!
Solution 1:[1]
I had the same problem, but only with newer verions of spring cloud. With older ones, it worked as expected.
It turned out, that it was connected to the use of the import: 'configserver:' settings. I had all my config in the application.yml:
spring:
config:
import: 'configserver:'
application:
name: my-app
cloud:
config:
uri: https://localhost:1234
name: my-app
label: ${profile}
and no bootstrap.yml at all. I just provided the spring.cloud.bootstrap.enabled=true as an environment variable so that the SSLConfigServiceBootstrapConfiguration was read in.
After I moved the config-client settings (see code-snippet above) into the bootstrap.yml and removed the spring.config.import: 'configserver', it works as expected:
bootstrap.yml:
spring:
application:
name: my-app
cloud:
config:
uri: https://localhost:1234
name: my-app
label: ${profile}
Cheers.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | ET Spielberg |
