Enable skipping ssl validation for vault (#989)

This commit is contained in:
Dylan Roberts
2018-05-03 11:48:08 -04:00
committed by Spencer Gibb
parent bb4d6b08a4
commit 0fd0c7b801
6 changed files with 258 additions and 72 deletions

View File

@@ -15,10 +15,19 @@
*/
package org.springframework.cloud.config.server.config;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Optional;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.eclipse.jgit.api.TransportConfigCallback;
import org.tmatesoft.svn.core.SVNException;
@@ -59,7 +68,9 @@ import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.client.RestTemplate;
/**
* @author Dave Syer
@@ -101,6 +112,75 @@ public class EnvironmentRepositoryConfiguration {
return new EnvironmentWatch.Default();
}
}
@Configuration
@ConditionalOnClass(TransportConfigCallback.class)
static class JGitFactoryConfig {
@Bean
public MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory(
ConfigurableEnvironment environment, ConfigServerProperties server,
Optional<TransportConfigCallback> customTransportConfigCallback) {
return new MultipleJGitEnvironmentRepositoryFactory(environment, server, customTransportConfigCallback);
}
}
@Configuration
@ConditionalOnClass(SVNException.class)
static class SvnFactoryConfig {
@Bean
public SvnEnvironmentRepositoryFactory svnEnvironmentRepositoryFactory(ConfigurableEnvironment environment,
ConfigServerProperties server) {
return new SvnEnvironmentRepositoryFactory(environment, server);
}
}
@Configuration
static class VaultFactoryConfig {
@Bean
public VaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory(
ObjectProvider<HttpServletRequest> request, EnvironmentWatch watch,
Optional<RestTemplate> skipSslValidationRestTemplate) {
return new VaultEnvironmentRepositoryFactory(request, watch, skipSslValidationRestTemplate);
}
}
@Configuration
@ConditionalOnClass(JdbcTemplate.class)
static class JdbcCompositeConfig {
@Bean
@ConditionalOnBean(JdbcTemplate.class)
public JdbcEnvironmentRepositoryFactory jdbcEnvironmentRepositoryFactory(JdbcTemplate jdbc) {
return new JdbcEnvironmentRepositoryFactory(jdbc);
}
}
@Configuration
static class NativeFactoryConfig {
@Bean
public NativeEnvironmentRepositoryFactory nativeEnvironmentRepositoryFactory(ConfigurableEnvironment environment,
ConfigServerProperties properties) {
return new NativeEnvironmentRepositoryFactory(environment, properties);
}
}
@Bean
@ConditionalOnClass(HttpClient.class)
public RestTemplate skipSslValidationRestTemplate() {
try {
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, (certificate, authType) -> true)
.build();
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return new RestTemplate(requestFactory);
} catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
throw new RuntimeException(e);
}
}
}
@Configuration
@@ -117,10 +197,8 @@ class DefaultRepositoryConfiguration {
@Bean
public MultipleJGitEnvironmentRepository defaultEnvironmentRepository(
MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory,
MultipleJGitEnvironmentProperties environmentProperties) {
MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory =
new MultipleJGitEnvironmentRepositoryFactory(environment, server,
Optional.ofNullable(customTransportConfigCallback));
return gitEnvironmentRepositoryFactory.build(environmentProperties);
}
}
@@ -129,19 +207,11 @@ class DefaultRepositoryConfiguration {
@ConditionalOnMissingBean(EnvironmentRepository.class)
@Profile("native")
class NativeRepositoryConfiguration {
@Autowired
private ConfigurableEnvironment environment;
@Autowired
private ConfigServerProperties configServerProperties;
@Bean
public NativeEnvironmentRepository nativeEnvironmentRepository(
public NativeEnvironmentRepository nativeEnvironmentRepository(NativeEnvironmentRepositoryFactory factory,
NativeEnvironmentProperties environmentProperties) {
NativeEnvironmentRepository repository = new NativeEnvironmentRepository(this.environment,
environmentProperties);
repository.setDefaultLabel(configServerProperties.getDefaultLabel());
return repository;
return factory.build(environmentProperties);
}
}
@@ -153,25 +223,22 @@ class GitRepositoryConfiguration extends DefaultRepositoryConfiguration {
@Configuration
@Profile("subversion")
class SvnRepositoryConfiguration {
@Autowired
private ConfigurableEnvironment environment;
@Autowired
private ConfigServerProperties server;
@Bean
public SvnKitEnvironmentRepository svnKitEnvironmentRepository(SvnKitEnvironmentProperties environmentProperties) {
return new SvnEnvironmentRepositoryFactory(environment, server).build(environmentProperties);
public SvnKitEnvironmentRepository svnKitEnvironmentRepository(SvnKitEnvironmentProperties environmentProperties,
SvnEnvironmentRepositoryFactory factory) {
return factory.build(environmentProperties);
}
}
@Configuration
@Profile("vault")
class VaultRepositoryConfiguration {
@Bean
public VaultEnvironmentRepository vaultEnvironmentRepository(ObjectProvider<HttpServletRequest> request, EnvironmentWatch watch,
VaultEnvironmentProperties environmentProperties) {
return new VaultEnvironmentRepositoryFactory(request, watch).build(environmentProperties);
public VaultEnvironmentRepository vaultEnvironmentRepository(VaultEnvironmentRepositoryFactory factory,
VaultEnvironmentProperties environmentProperties) {
return factory.build(environmentProperties);
}
}
@@ -182,9 +249,9 @@ class JdbcRepositoryConfiguration {
@Bean
@ConditionalOnBean(JdbcTemplate.class)
public JdbcEnvironmentRepository jdbcEnvironmentRepository(JdbcTemplate jdbc,
public JdbcEnvironmentRepository jdbcEnvironmentRepository(JdbcEnvironmentRepositoryFactory factory,
JdbcEnvironmentProperties environmentProperties) {
return new JdbcEnvironmentRepositoryFactory(jdbc).build(environmentProperties);
return factory.build(environmentProperties);
}
}
@@ -192,47 +259,6 @@ class JdbcRepositoryConfiguration {
@Profile("composite")
class CompositeRepositoryConfiguration {
@Configuration
@ConditionalOnClass(TransportConfigCallback.class)
static class JGitCompositeConfig {
@Bean
public MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory(
ConfigurableEnvironment environment, ConfigServerProperties server,
Optional<TransportConfigCallback> customTransportConfigCallback) {
return new MultipleJGitEnvironmentRepositoryFactory(environment, server, customTransportConfigCallback);
}
}
@Configuration
@ConditionalOnClass(SVNException.class)
static class SvnCompositeConfig {
@Bean
public SvnEnvironmentRepositoryFactory svnEnvironmentRepositoryFactory(ConfigurableEnvironment environment,
ConfigServerProperties server) {
return new SvnEnvironmentRepositoryFactory(environment, server);
}
}
@Bean
public VaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory(ObjectProvider<HttpServletRequest> request,
EnvironmentWatch watch) {
return new VaultEnvironmentRepositoryFactory(request, watch);
}
@Configuration
@ConditionalOnClass(JdbcTemplate.class)
static class JdbcCompositeConfig {
@Bean
public JdbcEnvironmentRepositoryFactory jdbcEnvironmentRepositoryFactory(JdbcTemplate jdbc) {
return new JdbcEnvironmentRepositoryFactory(jdbc);
}
}
@Bean
public NativeEnvironmentRepositoryFactory nativeEnvironmentRepositoryFactory(ConfigurableEnvironment environment) {
return new NativeEnvironmentRepositoryFactory(environment);
}
@Bean
public static CompositeEnvironmentBeanFactoryPostProcessor compositeEnvironmentRepositoryBeanFactoryPostProcessor(
Environment environment) {

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.cloud.config.server.environment;
import org.springframework.cloud.config.server.config.ConfigServerProperties;
import org.springframework.core.env.ConfigurableEnvironment;
/**
@@ -23,13 +24,17 @@ import org.springframework.core.env.ConfigurableEnvironment;
public class NativeEnvironmentRepositoryFactory implements EnvironmentRepositoryFactory<NativeEnvironmentRepository,
NativeEnvironmentProperties> {
private ConfigurableEnvironment environment;
private ConfigServerProperties properties;
public NativeEnvironmentRepositoryFactory(ConfigurableEnvironment environment) {
public NativeEnvironmentRepositoryFactory(ConfigurableEnvironment environment, ConfigServerProperties properties) {
this.environment = environment;
this.properties = properties;
}
@Override
public NativeEnvironmentRepository build(NativeEnvironmentProperties environmentProperties) {
return new NativeEnvironmentRepository(environment, environmentProperties);
NativeEnvironmentRepository repository = new NativeEnvironmentRepository(environment, environmentProperties);
repository.setDefaultLabel(properties.getDefaultLabel());
return repository;
}
}

View File

@@ -36,6 +36,11 @@ public class VaultEnvironmentProperties implements EnvironmentRepositoryProperti
private String defaultKey = "application";
/** Vault profile separator. Defaults to comma. */
private String profileSeparator = ",";
/**
* Flag to indicate that SSL certificate validation should be bypassed when communicating with a repository served
* over an HTTPS connection.
*/
private boolean skipSslValidation = false;
private int order = Ordered.LOWEST_PRECEDENCE;
public String getHost() {
@@ -86,6 +91,14 @@ public class VaultEnvironmentProperties implements EnvironmentRepositoryProperti
this.profileSeparator = profileSeparator;
}
public boolean isSkipSslValidation() {
return skipSslValidation;
}
public void setSkipSslValidation(boolean skipSslValidation) {
this.skipSslValidation = skipSslValidation;
}
public int getOrder() {
return order;
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.cloud.config.server.environment;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.ObjectProvider;
@@ -27,16 +28,21 @@ public class VaultEnvironmentRepositoryFactory implements EnvironmentRepositoryF
VaultEnvironmentProperties> {
private ObjectProvider<HttpServletRequest> request;
private EnvironmentWatch watch;
private Optional<RestTemplate> skipSslValidationRestTemplate;
public VaultEnvironmentRepositoryFactory(ObjectProvider<HttpServletRequest> request, EnvironmentWatch watch) {
public VaultEnvironmentRepositoryFactory(ObjectProvider<HttpServletRequest> request, EnvironmentWatch watch,
Optional<RestTemplate> skipSslValidationRestTemplate) {
this.request = request;
this.watch = watch;
this.skipSslValidationRestTemplate = skipSslValidationRestTemplate;
}
@Override
public VaultEnvironmentRepository build(VaultEnvironmentProperties environmentProperties) {
VaultEnvironmentRepository repository = new VaultEnvironmentRepository(request, watch, new RestTemplate(),
environmentProperties);
return repository;
if (environmentProperties.isSkipSslValidation() && skipSslValidationRestTemplate.isPresent()) {
return new VaultEnvironmentRepository(request, watch, skipSslValidationRestTemplate.get(),
environmentProperties);
}
return new VaultEnvironmentRepository(request, watch, new RestTemplate(), environmentProperties);
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.config.server.environment;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Optional;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.servlet.http.HttpServletRequest;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* author Dylan Roberts
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = VaultEnvironmentRepositoryIntegrationTests.TestApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {
"server.ssl.key-store=classpath:ssl-test.jks",
"server.ssl.key-store-password=password",
"server.ssl.key-password=password",
"server.key-alias=ssl-test"})
public class VaultEnvironmentRepositoryIntegrationTests {
@LocalServerPort
private String localServerPort;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void withSslValidation() {
VaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory =
new VaultEnvironmentRepositoryFactory(withRequest(), new EnvironmentWatch.Default(),
Optional.of(skipSslValidationRestTemplate()));
VaultEnvironmentRepository vaultEnvironmentRepository =
vaultEnvironmentRepositoryFactory.build(withEnvironmentProperties(false));
expectedException.expectCause(instanceOf(SSLHandshakeException.class));
vaultEnvironmentRepository.findOne("application", "profile", "label");
}
@Test
public void skipSslValidation() {
VaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory =
new VaultEnvironmentRepositoryFactory(withRequest(), new EnvironmentWatch.Default(),
Optional.of(skipSslValidationRestTemplate()));
VaultEnvironmentRepository vaultEnvironmentRepository =
vaultEnvironmentRepositoryFactory.build(withEnvironmentProperties(true));
Environment actual = vaultEnvironmentRepository.findOne("application", "profile", "label");
assertThat(actual).isNotNull();
}
private RestTemplate skipSslValidationRestTemplate() {
try {
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, (certificate, authType) -> true)
.build();
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return new RestTemplate(requestFactory);
} catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
throw new RuntimeException(e);
}
}
private VaultEnvironmentProperties withEnvironmentProperties(boolean skipSslValidation) {
VaultEnvironmentProperties environmentProperties = new VaultEnvironmentProperties();
environmentProperties.setPort(Integer.decode(localServerPort));
environmentProperties.setScheme("https");
environmentProperties.setSkipSslValidation(skipSslValidation);
return environmentProperties;
}
private ObjectProvider<HttpServletRequest> withRequest() {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getHeader("X-Config-Token")).thenReturn("configToken");
ObjectProvider<HttpServletRequest> requestProvider = mock(ObjectProvider.class);
when(requestProvider.getIfAvailable()).thenReturn(request);
return requestProvider;
}
@SpringBootConfiguration
@EnableAutoConfiguration
public static class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
}