Add SSL trust-store configuration.
Spring Cloud Vault Config now supports property-based SSL configuration using spring.cloud.vault.ssl.* properties to enable server certificate validation. Introducing a Vault-specific configuration requires to configure HTTP clients individually and so this change adds configuration support for Apache HTTP Components, Netty and the OkHttp client. Clients are picked configured if they exist on the class path. Fixes gh-7
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,3 +17,4 @@ _site/
|
||||
download/
|
||||
vault/download
|
||||
vault/vault
|
||||
work
|
||||
|
||||
@@ -13,6 +13,7 @@ install:
|
||||
- tar xzf download/apache-cassandra-2.2.6-bin.tar.gz
|
||||
- cp -f src/test/resources/cassandra.yaml apache-cassandra-2.2.6/conf
|
||||
- apache-cassandra-2.2.6/bin/cassandra
|
||||
- src/test/bash/create_certificates.sh
|
||||
- src/test/bash/install_vault.sh
|
||||
- src/test/bash/local_run_vault.sh &
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Spring Cloud Vault Config provides client-side support for externalized configur
|
||||
|
||||
== Features
|
||||
|
||||
=== Spring Cloud Vauld Config Client
|
||||
=== Spring Cloud Vault Config Client
|
||||
|
||||
Specifically for Spring applications:
|
||||
|
||||
@@ -162,6 +162,12 @@ spring.cloud.vault:
|
||||
|
||||
== Building
|
||||
|
||||
==== Build requirements for Vault
|
||||
|
||||
Spring Cloud Vault Config requires SSL certificates and a running
|
||||
Vault instance listening on `localhost:8200`. Certificates and the Vault
|
||||
setup are scripted, the scripts are located in `src/test/bash`.
|
||||
|
||||
:jdkversion: 1.7
|
||||
|
||||
=== Basic Compile and Test
|
||||
|
||||
@@ -3,7 +3,7 @@ include::intro.adoc[]
|
||||
|
||||
== Features
|
||||
|
||||
=== Spring Cloud Vauld Config Client
|
||||
=== Spring Cloud Vault Config Client
|
||||
|
||||
Specifically for Spring applications:
|
||||
|
||||
@@ -18,6 +18,12 @@ include::quickstart.adoc[]
|
||||
|
||||
== Building
|
||||
|
||||
==== Build requirements for Vault
|
||||
|
||||
Spring Cloud Vault Config requires SSL certificates and a running
|
||||
Vault instance listening on `localhost:8200`. Certificates and the Vault
|
||||
setup are scripted, the scripts are located in `src/test/bash`.
|
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building.adoc[]
|
||||
|
||||
== Contributing
|
||||
|
||||
@@ -220,3 +220,24 @@ it cannot connect to the Vault Server. If this is the desired
|
||||
behavior, set the bootstrap configuration property
|
||||
`spring.cloud.vault.failFast=true` and the client will halt with
|
||||
an Exception.
|
||||
|
||||
[[vault-client-ssl]]
|
||||
== Vault Client SSL configuration
|
||||
|
||||
SSL can be configured declaratively by setting various properties.
|
||||
You can set either `javax.net.ssl.trustStore` to configure
|
||||
JVM-wide SSL settings or `spring.cloud.vault.ssl.trust-store`
|
||||
to set SSL settings only for Spring Cloud Vault Config.
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
spring.cloud.vault:
|
||||
ssl:
|
||||
trust-store: classpath:keystore.jks
|
||||
trust-store-password: changeit
|
||||
----
|
||||
|
||||
Please note that configuring `spring.cloud.vault.ssl.*` can be only
|
||||
applied when either Apache Http Components, netty or the OkHttp client
|
||||
is on your class-path.
|
||||
|
||||
|
||||
45
pom.xml
45
pom.xml
@@ -22,6 +22,10 @@
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.7</java.version>
|
||||
<httpclient.version>4.5.2</httpclient.version>
|
||||
<httpcore.version>4.4.4</httpcore.version>
|
||||
<netty.version>4.1.0.Final</netty.version>
|
||||
<okhttp.version>2.7.5</okhttp.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -54,6 +58,41 @@
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>${httpclient.version}</version>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
<version>${httpcore.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>${okhttp.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
@@ -66,9 +105,9 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.38</version>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.38</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright 2016 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.vault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.OkHttpClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* Factory for {@link ClientHttpRequestFactory} that supports Apache HTTP Components,
|
||||
* OkHttp, Netty and the JDK HTTP client (in that order). This factory configures a
|
||||
* {@link ClientHttpRequestFactory} depending on the available dependencies.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CommonsLog
|
||||
class ClientHttpRequestFactoryFactory {
|
||||
|
||||
private final static boolean HTTP_COMPONENTS_PRESENT = ClassUtils.isPresent(
|
||||
"org.apache.http.client.HttpClient",
|
||||
ClientHttpRequestFactoryFactory.class.getClassLoader());
|
||||
|
||||
private final static boolean OKHTTP_PRESENT = ClassUtils.isPresent(
|
||||
"com.squareup.okhttp.OkHttpClient",
|
||||
ClientHttpRequestFactoryFactory.class.getClassLoader());
|
||||
|
||||
private final static boolean NETTY_PRESENT = ClassUtils.isPresent(
|
||||
"io.netty.channel.nio.NioEventLoopGroup",
|
||||
ClientHttpRequestFactoryFactory.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* Creates a {@link ClientHttpRequestFactory} for the given {@link VaultProperties}.
|
||||
*
|
||||
* @param vaultProperties must not be {@literal null}
|
||||
* @return a new {@link ClientHttpRequestFactory}. Lifecycle beans must be initialized
|
||||
* after obtaining.
|
||||
*/
|
||||
public static ClientHttpRequestFactory create(VaultProperties vaultProperties) {
|
||||
|
||||
try {
|
||||
|
||||
if (HTTP_COMPONENTS_PRESENT) {
|
||||
return usingHttpComponents(vaultProperties);
|
||||
}
|
||||
|
||||
if (OKHTTP_PRESENT) {
|
||||
return usingOkHttp(vaultProperties);
|
||||
}
|
||||
|
||||
if (NETTY_PRESENT) {
|
||||
return usingNetty(vaultProperties);
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException | GeneralSecurityException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
if (hasSslConfiguration(vaultProperties)) {
|
||||
log.warn("VaultProperties has SSL configured but the SSL configuration "
|
||||
+ "must be applied outside the Vault Client to use the JDK HTTP client");
|
||||
}
|
||||
|
||||
return new SimpleClientHttpRequestFactory();
|
||||
}
|
||||
|
||||
protected static ClientHttpRequestFactory usingHttpComponents(
|
||||
VaultProperties vaultProperties)
|
||||
throws GeneralSecurityException, IOException {
|
||||
|
||||
HttpClientBuilder httpClientBuilder = HttpClients.custom();
|
||||
|
||||
if (hasSslConfiguration(vaultProperties)) {
|
||||
|
||||
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
|
||||
getSSLContext(vaultProperties.getSsl()));
|
||||
httpClientBuilder.setSSLSocketFactory(sslSocketFactory);
|
||||
}
|
||||
|
||||
RequestConfig requestConfig = RequestConfig.custom() //
|
||||
.setConnectTimeout(vaultProperties.getConnectionTimeout()) //
|
||||
.setSocketTimeout(vaultProperties.getReadTimeout()) //
|
||||
.build();
|
||||
|
||||
httpClientBuilder.setDefaultRequestConfig(requestConfig);
|
||||
|
||||
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(
|
||||
httpClientBuilder.build());
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
protected static ClientHttpRequestFactory usingNetty(VaultProperties vaultProperties)
|
||||
throws GeneralSecurityException, IOException {
|
||||
|
||||
VaultProperties.Ssl ssl = vaultProperties.getSsl();
|
||||
|
||||
final Netty4ClientHttpRequestFactory requestFactory = new Netty4ClientHttpRequestFactory();
|
||||
|
||||
if (hasSslConfiguration(vaultProperties)) {
|
||||
|
||||
SslContext sslContext = SslContextBuilder //
|
||||
.forClient() //
|
||||
.trustManager(createTrustManagerFactory(ssl.getTrustStore(),
|
||||
ssl.getTrustStorePassword())) //
|
||||
.sslProvider(SslProvider.JDK) //
|
||||
.build();
|
||||
|
||||
requestFactory.setSslContext(sslContext);
|
||||
}
|
||||
|
||||
requestFactory.setConnectTimeout(vaultProperties.getConnectionTimeout());
|
||||
requestFactory.setReadTimeout(vaultProperties.getReadTimeout());
|
||||
|
||||
return requestFactory;
|
||||
}
|
||||
|
||||
protected static ClientHttpRequestFactory usingOkHttp(VaultProperties vaultProperties)
|
||||
throws GeneralSecurityException, IOException {
|
||||
|
||||
final OkHttpClient okHttpClient = new OkHttpClient();
|
||||
|
||||
OkHttpClientHttpRequestFactory requestFactory = new OkHttpClientHttpRequestFactory(
|
||||
okHttpClient) {
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
|
||||
if (okHttpClient.getCache() != null) {
|
||||
okHttpClient.getCache().close();
|
||||
}
|
||||
|
||||
okHttpClient.getDispatcher().getExecutorService().shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
if (hasSslConfiguration(vaultProperties)) {
|
||||
okHttpClient.setSslSocketFactory(
|
||||
getSSLContext(vaultProperties.getSsl()).getSocketFactory());
|
||||
}
|
||||
|
||||
requestFactory.setConnectTimeout(vaultProperties.getConnectionTimeout());
|
||||
requestFactory.setReadTimeout(vaultProperties.getReadTimeout());
|
||||
|
||||
return requestFactory;
|
||||
}
|
||||
|
||||
private static SSLContext getSSLContext(VaultProperties.Ssl ssl)
|
||||
throws GeneralSecurityException, IOException {
|
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, createTrustManagerFactory(ssl.getTrustStore(),
|
||||
ssl.getTrustStorePassword()).getTrustManagers(), null);
|
||||
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
private static TrustManagerFactory createTrustManagerFactory(Resource trustFile,
|
||||
String storePassword) throws GeneralSecurityException, IOException {
|
||||
|
||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
InputStream inputStream = trustFile.getInputStream();
|
||||
|
||||
try {
|
||||
trustStore.load(inputStream, StringUtils.hasText(storePassword)
|
||||
? storePassword.toCharArray() : null);
|
||||
}
|
||||
finally {
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(trustStore);
|
||||
|
||||
return trustManagerFactory;
|
||||
}
|
||||
|
||||
private static boolean hasSslConfiguration(VaultProperties vaultProperties) {
|
||||
return vaultProperties.getSsl() != null
|
||||
&& vaultProperties.getSsl().getTrustStore() != null;
|
||||
}
|
||||
}
|
||||
@@ -19,14 +19,17 @@ package org.springframework.cloud.vault;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -37,10 +40,23 @@ import org.springframework.util.ClassUtils;
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
public class VaultBootstrapConfiguration {
|
||||
|
||||
@Bean
|
||||
@Qualifier("vault-ClientHttpRequestFactory")
|
||||
public ClientHttpRequestFactory clientHttpRequestFactory(){
|
||||
return ClientHttpRequestFactoryFactory.create(vaultProperties());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("vault-RestTemplate")
|
||||
public RestTemplate restTemplate(){
|
||||
return new RestTemplate(clientHttpRequestFactory());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VaultClient vaultClient(ApplicationContext applicationContext) {
|
||||
|
||||
VaultClient vaultClient = new VaultClient(vaultProperties());
|
||||
vaultClient.setRest(restTemplate());
|
||||
|
||||
Map<String, AppIdUserIdMechanism> appIdUserIdMechanisms = applicationContext
|
||||
.getBeansOfType(AppIdUserIdMechanism.class);
|
||||
|
||||
@@ -13,37 +13,40 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
import static com.sun.org.apache.xalan.internal.xsltc.compiler.sym.error;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.Value;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cloud.vault.VaultProperties.AppIdProperties;
|
||||
import org.springframework.cloud.vault.VaultProperties.AuthenticationMethod;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.HttpServerErrorException;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import lombok.Setter;
|
||||
import lombok.Value;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* Vault client. This client reads data from Vault secret backends and can authenticate with
|
||||
* Vault to obtain an access token.
|
||||
* Vault client. This client reads data from Vault secret backends and can authenticate
|
||||
* with Vault to obtain an access token.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@CommonsLog
|
||||
public class VaultClient {
|
||||
|
||||
@@ -56,9 +59,18 @@ public class VaultClient {
|
||||
@Setter
|
||||
private AppIdUserIdMechanism appIdUserIdMechanism;
|
||||
|
||||
private ClientHttpRequestFactory clientHttpRequestFactory;
|
||||
private final VaultProperties properties;
|
||||
|
||||
public Map<String, String> read(SecureBackendAccessor secureBackendAccessor, VaultToken vaultToken) {
|
||||
public VaultClient(VaultProperties properties) {
|
||||
|
||||
Assert.notNull(properties, "VaultProperties must not be null");
|
||||
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public Map<String, String> read(SecureBackendAccessor secureBackendAccessor,
|
||||
VaultToken vaultToken) {
|
||||
|
||||
Assert.notNull(secureBackendAccessor, "SecureBackendAccessor must not be empty!");
|
||||
Assert.notNull(vaultToken, "VaultToken must not be null!");
|
||||
@@ -69,23 +81,25 @@ public class VaultClient {
|
||||
Exception error = null;
|
||||
String errorBody = null;
|
||||
|
||||
log.info(String.format("Fetching config from server at: %s", url));
|
||||
URI uri = this.rest.getUriTemplateHandler().expand(url, secureBackendAccessor.variables());
|
||||
log.info(String.format("Fetching config from server at: %s", uri));
|
||||
|
||||
try {
|
||||
ResponseEntity<VaultResponse> response = this.rest.exchange(url,
|
||||
HttpMethod.GET, new HttpEntity<>(headers), VaultResponse.class,
|
||||
secureBackendAccessor.variables());
|
||||
ResponseEntity<VaultResponse> response = this.rest.exchange(uri,
|
||||
HttpMethod.GET, new HttpEntity<>(headers), VaultResponse.class);
|
||||
|
||||
HttpStatus status = response.getStatusCode();
|
||||
if (status == HttpStatus.OK) {
|
||||
if (response.getBody().getData() != null) {
|
||||
return secureBackendAccessor.transformProperties(response.getBody().getData());
|
||||
return secureBackendAccessor
|
||||
.transformProperties(response.getBody().getData());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (HttpServerErrorException e) {
|
||||
error = e;
|
||||
if (MediaType.APPLICATION_JSON.includes(e.getResponseHeaders()
|
||||
.getContentType())) {
|
||||
if (MediaType.APPLICATION_JSON
|
||||
.includes(e.getResponseHeaders().getContentType())) {
|
||||
errorBody = e.getResponseBodyAsString();
|
||||
}
|
||||
}
|
||||
@@ -99,8 +113,11 @@ public class VaultClient {
|
||||
error);
|
||||
}
|
||||
|
||||
log.warn(String.format("Could not locate PropertySource: %s"
|
||||
, (errorBody == null ? error==null ? "key not found" : error.getMessage() : errorBody)));
|
||||
log.warn(
|
||||
String.format("Could not locate PropertySource: %s",
|
||||
(errorBody == null
|
||||
? error == null ? "key not found" : error.getMessage()
|
||||
: errorBody)));
|
||||
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
@@ -119,16 +136,20 @@ public class VaultClient {
|
||||
*/
|
||||
public VaultToken createToken() {
|
||||
|
||||
if (properties.getAuthentication() == AuthenticationMethod.APPID && appIdUserIdMechanism != null) {
|
||||
if (properties.getAuthentication() == AuthenticationMethod.APPID
|
||||
&& appIdUserIdMechanism != null) {
|
||||
AppIdProperties appId = properties.getAppId();
|
||||
return createTokenUsingAppId(new AppIdTuple(properties.getApplicationName(), appIdUserIdMechanism.createUserId()), appId);
|
||||
return createTokenUsingAppId(new AppIdTuple(properties.getApplicationName(),
|
||||
appIdUserIdMechanism.createUserId()), appId);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"Cannot create a token for auth method %s", properties.getAuthentication()));
|
||||
throw new UnsupportedOperationException(
|
||||
String.format("Cannot create a token for auth method %s",
|
||||
properties.getAuthentication()));
|
||||
}
|
||||
|
||||
private VaultToken createTokenUsingAppId(AppIdTuple appIdTuple, AppIdProperties appId) {
|
||||
private VaultToken createTokenUsingAppId(AppIdTuple appIdTuple,
|
||||
AppIdProperties appId) {
|
||||
|
||||
String url = buildUrl();
|
||||
Map<String, String> variables = new HashMap<>();
|
||||
@@ -151,7 +172,8 @@ public class VaultClient {
|
||||
String token = (String) body.getAuth().get("client_token");
|
||||
|
||||
return VaultToken.of(token, body.getLeaseDuration());
|
||||
} catch (HttpClientErrorException e) {
|
||||
}
|
||||
catch (HttpClientErrorException e) {
|
||||
|
||||
if (e.getStatusCode().equals(HttpStatus.BAD_REQUEST)) {
|
||||
throw new IllegalStateException(String.format(
|
||||
@@ -176,7 +198,7 @@ public class VaultClient {
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class AppIdTuple{
|
||||
private static class AppIdTuple {
|
||||
private String appId;
|
||||
private String userId;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
@@ -21,6 +20,7 @@ import org.hibernate.validator.constraints.Range;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -39,7 +39,7 @@ public class VaultProperties {
|
||||
* Vault server host.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String host = "127.0.0.1";
|
||||
private String host = "localhost";
|
||||
|
||||
/**
|
||||
* Vault server port.
|
||||
@@ -50,7 +50,7 @@ public class VaultProperties {
|
||||
/**
|
||||
* Protocol scheme. Can be either "http" or "https".
|
||||
*/
|
||||
private String scheme = "http";
|
||||
private String scheme = "https";
|
||||
|
||||
/**
|
||||
* Name of the default backend.
|
||||
@@ -70,6 +70,16 @@ public class VaultProperties {
|
||||
@NotEmpty
|
||||
private String profileSeparator = ",";
|
||||
|
||||
/**
|
||||
* Connection timeout;
|
||||
*/
|
||||
private int connectionTimeout = 5000;
|
||||
|
||||
/**
|
||||
* Read timeout;
|
||||
*/
|
||||
private int readTimeout = 15000;
|
||||
|
||||
/**
|
||||
* Fail fast if data cannot be obtained from Vault.
|
||||
*/
|
||||
@@ -82,6 +92,8 @@ public class VaultProperties {
|
||||
|
||||
private AppIdProperties appId = new AppIdProperties();
|
||||
|
||||
private Ssl ssl = new Ssl();
|
||||
|
||||
private MySql mysql = new MySql();
|
||||
|
||||
private PostgreSql postgresql = new PostgreSql();
|
||||
@@ -129,6 +141,20 @@ public class VaultProperties {
|
||||
private String userId = MAC_ADDRESS;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Ssl {
|
||||
|
||||
/**
|
||||
* Trust store that holds SSL certificates.
|
||||
*/
|
||||
private Resource trustStore;
|
||||
|
||||
/**
|
||||
* Password used to access the trust store.
|
||||
*/
|
||||
private String trustStorePassword;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class MySql implements DatabaseSecretProperties {
|
||||
|
||||
|
||||
60
src/test/bash/create_certificates.sh
Executable file
60
src/test/bash/create_certificates.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
CA_DIR=work/ca
|
||||
JKS_FILE=work/keystore.jks
|
||||
if [[ -d work/ca ]] ; then
|
||||
rm -Rf ${CA_DIR}
|
||||
fi
|
||||
|
||||
if [[ -f ${JKS_FILE} ]] ; then
|
||||
rm -Rf ${JKS_FILE}
|
||||
fi
|
||||
|
||||
mkdir -p ${CA_DIR}/private ${CA_DIR}/certs ${CA_DIR}/crl ${CA_DIR}/csr ${CA_DIR}/newcerts ${CA_DIR}/intermediate
|
||||
|
||||
echo "[INFO] Generating CA private key"
|
||||
# Less bits = less secure = faster to generate
|
||||
openssl genrsa -passout pass:changeit -aes256 -out ${CA_DIR}/private/ca.key.pem 2048
|
||||
|
||||
chmod 400 ${CA_DIR}/private/ca.key.pem
|
||||
|
||||
echo "[INFO] Generating CA certificate"
|
||||
openssl req -config ${DIR}/openssl.cnf \
|
||||
-key ${CA_DIR}/private/ca.key.pem \
|
||||
-new -x509 -days 7300 -sha256 -extensions v3_ca \
|
||||
-out ${CA_DIR}/certs/ca.cert.pem \
|
||||
-passin pass:changeit \
|
||||
-subj "/C=NN/ST=Unknown/L=Unknown/O=spring-cloud-vault-config/CN=CA Certificate"
|
||||
|
||||
echo "[INFO] Prepare CA database"
|
||||
echo 1000 > ${CA_DIR}/serial
|
||||
touch ${CA_DIR}/index.txt
|
||||
|
||||
echo "[INFO] Generating server private key"
|
||||
openssl genrsa -aes256 \
|
||||
-passout pass:changeit \
|
||||
-out ${CA_DIR}/private/localhost.key.pem 2048
|
||||
|
||||
openssl rsa -in ${CA_DIR}/private/localhost.key.pem \
|
||||
-out ${CA_DIR}/private/localhost.decrypted.key.pem \
|
||||
-passin pass:changeit
|
||||
|
||||
chmod 400 ${CA_DIR}/private/localhost.key.pem
|
||||
|
||||
echo "[INFO] Generating server certificate request"
|
||||
openssl req -config ${DIR}/openssl.cnf \
|
||||
-key ${CA_DIR}/private/localhost.key.pem \
|
||||
-passin pass:changeit \
|
||||
-new -sha256 -out ${CA_DIR}/csr/localhost.csr.pem \
|
||||
-subj "/C=NN/ST=Unknown/L=Unknown/O=spring-cloud-vault-config/CN=localhost"
|
||||
|
||||
echo "[INFO] Signing certificate request"
|
||||
openssl ca -config ${DIR}/openssl.cnf \
|
||||
-extensions server_cert -days 375 -notext -md sha256 \
|
||||
-passin pass:changeit \
|
||||
-batch \
|
||||
-in ${CA_DIR}/csr/localhost.csr.pem \
|
||||
-out ${CA_DIR}/certs/localhost.cert.pem
|
||||
|
||||
${JAVA_HOME}/bin/keytool -importcert -keystore ${JKS_FILE} -file ${CA_DIR}/certs/ca.cert.pem -noprompt -storepass changeit
|
||||
@@ -1,9 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
###########################################################################
|
||||
# Vault environment settings. Source this file. #
|
||||
###########################################################################
|
||||
|
||||
export VAULT_TOKEN=00000000-0000-0000-0000-000000000000
|
||||
export VAULT_ADDR=http://localhost:8200
|
||||
export VAULT_SKIP_VERIFY=true
|
||||
export VAULT_ADDR=https://localhost:8200
|
||||
export VAULT_SKIP_VERIFY=false
|
||||
export VAULT_CAPATH=${DIR}/work/ca/certs/ca.cert.pem
|
||||
|
||||
106
src/test/bash/openssl.cnf
Normal file
106
src/test/bash/openssl.cnf
Normal file
@@ -0,0 +1,106 @@
|
||||
[ ca ]
|
||||
# `man ca`
|
||||
default_ca = CA_default
|
||||
|
||||
[ CA_default ]
|
||||
# Directory and file locations.
|
||||
dir = work/ca
|
||||
certs = $dir/certs
|
||||
crl_dir = $dir/crl
|
||||
new_certs_dir = $dir/newcerts
|
||||
database = $dir/index.txt
|
||||
serial = $dir/serial
|
||||
RANDFILE = $dir/private/.rand
|
||||
|
||||
# The root key and root certificate.
|
||||
private_key = $dir/private/ca.key.pem
|
||||
certificate = $dir/certs/ca.cert.pem
|
||||
|
||||
# For certificate revocation lists.
|
||||
crlnumber = $dir/crlnumber
|
||||
crl = $dir/crl/ca.crl.pem
|
||||
crl_extensions = crl_ext
|
||||
default_crl_days = 30
|
||||
|
||||
# SHA-1 is deprecated, so use SHA-2 instead.
|
||||
default_md = sha256
|
||||
|
||||
name_opt = ca_default
|
||||
cert_opt = ca_default
|
||||
default_days = 375
|
||||
preserve = no
|
||||
policy = policy_strict
|
||||
|
||||
[ policy_strict ]
|
||||
# The root CA should only sign intermediate certificates that match.
|
||||
# See the POLICY FORMAT section of `man ca`.
|
||||
countryName = match
|
||||
stateOrProvinceName = match
|
||||
organizationName = match
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
[ req ]
|
||||
# Options for the `req` tool (`man req`).
|
||||
default_bits = 2048
|
||||
distinguished_name = req_distinguished_name
|
||||
string_mask = utf8only
|
||||
|
||||
# SHA-1 is deprecated, so use SHA-2 instead.
|
||||
default_md = sha256
|
||||
|
||||
# Extension to add when the -x509 option is used.
|
||||
x509_extensions = v3_ca
|
||||
|
||||
[ req_distinguished_name ]
|
||||
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
|
||||
countryName = Country Name (2 letter code)
|
||||
stateOrProvinceName = State or Province Name
|
||||
localityName = Locality Name
|
||||
0.organizationName = Organization Name
|
||||
organizationalUnitName = Organizational Unit Name
|
||||
commonName = Common Name
|
||||
emailAddress = Email Address
|
||||
|
||||
# Optionally, specify some defaults.
|
||||
countryName_default = NN
|
||||
stateOrProvinceName_default = Vault Test
|
||||
localityName_default =
|
||||
0.organizationName_default = spring-cloud-vault-config
|
||||
#organizationalUnitName_default =
|
||||
#emailAddress_default = info@spring-cloud-vault-config.dummy
|
||||
|
||||
[ v3_ca ]
|
||||
# Extensions for a typical CA (`man x509v3_config`).
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer
|
||||
basicConstraints = critical, CA:true
|
||||
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
||||
|
||||
[ v3_intermediate_ca ]
|
||||
# Extensions for a typical intermediate CA (`man x509v3_config`).
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer
|
||||
basicConstraints = critical, CA:true, pathlen:0
|
||||
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
||||
|
||||
[ usr_cert ]
|
||||
# Extensions for client certificates (`man x509v3_config`).
|
||||
basicConstraints = CA:FALSE
|
||||
nsCertType = client, email
|
||||
nsComment = "OpenSSL Generated Client Certificate"
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid,issuer
|
||||
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth, emailProtection
|
||||
|
||||
[ server_cert ]
|
||||
# Extensions for server certificates (`man x509v3_config`).
|
||||
basicConstraints = CA:FALSE
|
||||
nsCertType = server
|
||||
nsComment = "OpenSSL Generated Server Certificate"
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid,issuer:always
|
||||
keyUsage = critical, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -28,14 +27,10 @@ import org.springframework.cloud.vault.util.Settings;
|
||||
*/
|
||||
public class AppIdAuthenticationIntegrationTests extends GenericSecretIntegrationTests {
|
||||
|
||||
private VaultClient vaultClient;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
super.setUp();
|
||||
|
||||
VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
this.vaultProperties = Settings.createVaultProperties();
|
||||
|
||||
AppIdProperties appId = configureAppIdProperties();
|
||||
vaultProperties.setApplicationName("myapp");
|
||||
@@ -51,9 +46,9 @@ public class AppIdAuthenticationIntegrationTests extends GenericSecretIntegratio
|
||||
prepare().mapAppId(vaultProperties.getApplicationName());
|
||||
prepare().mapUserId(vaultProperties.getApplicationName(), userId);
|
||||
|
||||
vaultClient = new VaultClient(vaultProperties);
|
||||
vaultClient.setAppIdUserIdMechanism(userIdMechanism);
|
||||
|
||||
this.vaultClient = new VaultClient(vaultProperties);
|
||||
this.vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
|
||||
this.vaultClient.setAppIdUserIdMechanism(userIdMechanism);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
@@ -50,6 +49,9 @@ public class AppIdAuthenticationMethodsIntegrationTests extends AbstractIntegrat
|
||||
|
||||
VaultClient vaultClient = new VaultClient(
|
||||
prepareAppIdAuthenticationMethod(AppIdProperties.IP_ADDRESS, "myapp"));
|
||||
|
||||
vaultClient.setRest(TestRestTemplateFactory.create(Settings.createVaultProperties()));
|
||||
|
||||
vaultClient.setAppIdUserIdMechanism(new IpAddressUserId());
|
||||
assertThat(vaultClient.createToken()).isNotNull();
|
||||
}
|
||||
@@ -60,6 +62,8 @@ public class AppIdAuthenticationMethodsIntegrationTests extends AbstractIntegrat
|
||||
VaultProperties vaultProperties = prepareAppIdAuthenticationMethod("my-user-id",
|
||||
"myapp");
|
||||
VaultClient vaultClient = new VaultClient(vaultProperties);
|
||||
vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
|
||||
|
||||
vaultClient.setAppIdUserIdMechanism(new StaticUserId(vaultProperties));
|
||||
assertThat(vaultClient.createToken()).isNotNull();
|
||||
}
|
||||
@@ -70,6 +74,7 @@ public class AppIdAuthenticationMethodsIntegrationTests extends AbstractIntegrat
|
||||
VaultProperties vaultProperties = prepareAppIdAuthenticationMethod(
|
||||
AppIdProperties.MAC_ADDRESS, "myapp");
|
||||
VaultClient vaultClient = new VaultClient(vaultProperties);
|
||||
vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
|
||||
|
||||
vaultClient.setAppIdUserIdMechanism(new MacAddressUserId(vaultProperties));
|
||||
assertThat(vaultClient.createToken()).isNotNull();
|
||||
@@ -86,12 +91,12 @@ public class AppIdAuthenticationMethodsIntegrationTests extends AbstractIntegrat
|
||||
vaultProperties.setApplicationName("foobar");
|
||||
|
||||
VaultClient vaultClient = new VaultClient(vaultProperties);
|
||||
vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
|
||||
vaultClient.setAppIdUserIdMechanism(new MacAddressUserId(vaultProperties));
|
||||
|
||||
vaultClient.createToken();
|
||||
|
||||
fail("Missing IllegalStateException");
|
||||
|
||||
}
|
||||
|
||||
private VaultProperties prepareAppIdAuthenticationMethod(String userId, String appId)
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
@@ -25,11 +24,11 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the cassandra secret backend. This test
|
||||
@@ -82,7 +81,7 @@ public class CassandraSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
String.format("%s/roles/%s", cassandra.getBackend(), cassandra.getRole()),
|
||||
Collections.singletonMap("creation_cql", CREATE_USER_AND_GRANT_CQL));
|
||||
|
||||
vaultClient.setRest(new RestTemplate());
|
||||
vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2016 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.vault;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.OkHttpClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ClientHttpRequestFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class ClientHttpRequestFactoryFactoryIntegrationTests {
|
||||
|
||||
private VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
private String url = String.format("%s://%s:%d/v1/sys/health?sealedcode=200",
|
||||
vaultProperties.getScheme(), vaultProperties.getHost(),
|
||||
vaultProperties.getPort());
|
||||
|
||||
@Test
|
||||
public void httpComponentsClientShouldWork() throws Exception {
|
||||
|
||||
ClientHttpRequestFactory factory = ClientHttpRequestFactoryFactory
|
||||
.usingHttpComponents(vaultProperties);
|
||||
RestTemplate template = new RestTemplate(factory);
|
||||
|
||||
String response = template.getForObject(url, String.class);
|
||||
|
||||
assertThat(factory).isInstanceOf(HttpComponentsClientHttpRequestFactory.class);
|
||||
assertThat(response).isNotNull().contains("initialized");
|
||||
|
||||
((DisposableBean) factory).destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nettyClientShouldWork() throws Exception {
|
||||
|
||||
ClientHttpRequestFactory factory = ClientHttpRequestFactoryFactory
|
||||
.usingNetty(vaultProperties);
|
||||
((InitializingBean) factory).afterPropertiesSet();
|
||||
RestTemplate template = new RestTemplate(factory);
|
||||
|
||||
String response = template.getForObject(url, String.class);
|
||||
|
||||
assertThat(factory).isInstanceOf(Netty4ClientHttpRequestFactory.class);
|
||||
assertThat(response).isNotNull().contains("initialized");
|
||||
|
||||
((DisposableBean) factory).destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void okHttpClientShouldWork() throws Exception {
|
||||
|
||||
ClientHttpRequestFactory factory = ClientHttpRequestFactoryFactory
|
||||
.usingOkHttp(vaultProperties);
|
||||
RestTemplate template = new RestTemplate(factory);
|
||||
|
||||
String response = template.getForObject(url, String.class);
|
||||
|
||||
assertThat(factory).isInstanceOf(OkHttpClientHttpRequestFactory.class);
|
||||
assertThat(response).isNotNull().contains("initialized");
|
||||
|
||||
((DisposableBean) factory).destroy();
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
@@ -25,7 +24,6 @@ import java.util.Map;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the generic secret backend.
|
||||
@@ -34,15 +32,15 @@ import org.springframework.web.client.RestTemplate;
|
||||
*/
|
||||
public class GenericSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
private VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
private VaultClient vaultClient = new VaultClient(vaultProperties);
|
||||
protected VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
protected VaultClient vaultClient = new VaultClient(vaultProperties);
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
vaultProperties.setFailFast(false);
|
||||
prepare().writeSecret("app-name", (Map) createData());
|
||||
vaultClient.setRest(new RestTemplate());
|
||||
vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -67,11 +65,7 @@ public class GenericSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
public void shouldFailOnFailFast() throws Exception {
|
||||
|
||||
vaultProperties.setFailFast(true);
|
||||
|
||||
Map<String, String> secretProperties = vaultClient
|
||||
.read(generic(vaultProperties, "missing"), createToken());
|
||||
|
||||
assertThat(secretProperties).isNull();
|
||||
vaultClient.read(generic(vaultProperties, "missing"), createToken());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
@@ -24,11 +23,11 @@ import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the mysql secret backend. This test
|
||||
@@ -72,7 +71,7 @@ public class MySqlSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
prepare().write(String.format("%s/roles/%s", mySql.getBackend(), mySql.getRole()),
|
||||
Collections.singletonMap("sql", CREATE_USER_AND_GRANT_SQL));
|
||||
|
||||
vaultClient.setRest(new RestTemplate());
|
||||
vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
@@ -28,7 +27,6 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the postgresql secret backend. This
|
||||
@@ -78,7 +76,7 @@ public class PostgreSqlSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
postgreSql.getRole()),
|
||||
Collections.singletonMap("sql", CREATE_USER_AND_GRANT_SQL));
|
||||
|
||||
vaultClient.setRest(new RestTemplate());
|
||||
vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -13,13 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.VaultToken;
|
||||
import org.springframework.cloud.vault.util.PrepareVault;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
|
||||
@@ -31,7 +27,7 @@ import org.springframework.cloud.vault.util.Settings;
|
||||
public class PrepareVaultTests {
|
||||
|
||||
private VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
private PrepareVault prepareVault = new PrepareVault(new TestRestTemplate());
|
||||
private PrepareVault prepareVault = new PrepareVault(TestRestTemplateFactory.create(vaultProperties));
|
||||
|
||||
@Test
|
||||
public void initializeShouldCreateANewVault() throws Exception {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2016 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.vault;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
/**
|
||||
* Factory for {@link TestRestTemplate}. The template caches the
|
||||
* {@link ClientHttpRequestFactory} once it was initialized. Changes to timeouts or the
|
||||
* SSL configuration won't be applied once a {@link ClientHttpRequestFactory} was created
|
||||
* for the first time.
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class TestRestTemplateFactory {
|
||||
|
||||
private final static AtomicReference<ClientHttpRequestFactory> factoryCache = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* @param vaultProperties
|
||||
* @return
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static TestRestTemplate create(VaultProperties vaultProperties) {
|
||||
|
||||
initializeClientHttpRequestFactory(vaultProperties);
|
||||
|
||||
TestRestTemplate testRestTemplate = new TestRestTemplate();
|
||||
testRestTemplate.setRequestFactory(factoryCache.get());
|
||||
|
||||
return testRestTemplate;
|
||||
}
|
||||
|
||||
protected static void initializeClientHttpRequestFactory(
|
||||
VaultProperties vaultProperties) throws Exception {
|
||||
|
||||
if (factoryCache.get() != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ClientHttpRequestFactory clientHttpRequestFactory = ClientHttpRequestFactoryFactory
|
||||
.create(vaultProperties);
|
||||
|
||||
if (factoryCache.compareAndSet(null, clientHttpRequestFactory)) {
|
||||
|
||||
if (clientHttpRequestFactory instanceof InitializingBean) {
|
||||
((InitializingBean) clientHttpRequestFactory).afterPropertiesSet();
|
||||
}
|
||||
|
||||
if (clientHttpRequestFactory instanceof DisposableBean) {
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(
|
||||
new Thread("ClientHttpRequestFactory Shutdown Hook") {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
((DisposableBean) clientHttpRequestFactory).destroy();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault.util;
|
||||
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.VaultToken;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
|
||||
/**
|
||||
* Utility to retrieve settings during test.
|
||||
@@ -33,8 +33,9 @@ public class Settings {
|
||||
public static VaultProperties createVaultProperties() {
|
||||
|
||||
VaultProperties vaultProperties = new VaultProperties();
|
||||
vaultProperties.getSsl().setTrustStorePassword("changeit");
|
||||
vaultProperties.getSsl().setTrustStore(new FileSystemResource("work/keystore.jks"));
|
||||
vaultProperties.setToken(token().getToken());
|
||||
vaultProperties.setHost(System.getProperty("vault.host", "localhost"));
|
||||
|
||||
return vaultProperties;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault.util;
|
||||
|
||||
import java.net.InetAddress;
|
||||
@@ -21,7 +20,7 @@ import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.cloud.vault.TestRestTemplateFactory;
|
||||
import org.springframework.cloud.vault.VaultProperties;
|
||||
import org.springframework.cloud.vault.VaultToken;
|
||||
|
||||
@@ -33,14 +32,16 @@ import org.springframework.cloud.vault.VaultToken;
|
||||
public class VaultRule extends ExternalResource {
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
private final PrepareVault prepareVault = new PrepareVault(new TestRestTemplate());
|
||||
private final PrepareVault prepareVault;
|
||||
|
||||
public VaultRule() {
|
||||
this(Settings.createVaultProperties());
|
||||
}
|
||||
|
||||
public VaultRule(VaultProperties vaultProperties) {
|
||||
|
||||
this.vaultProperties = vaultProperties;
|
||||
this.prepareVault = new PrepareVault(TestRestTemplateFactory.create(vaultProperties));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
spring:
|
||||
application.name: testVaultApp
|
||||
cloud.vault.token: 00000000-0000-0000-0000-000000000000
|
||||
cloud.vault.ssl.trust-store: file:work/keystore.jks
|
||||
cloud.vault.ssl.trust-store-password: changeit
|
||||
|
||||
@@ -3,7 +3,8 @@ backend "inmem" {
|
||||
|
||||
listener "tcp" {
|
||||
address = "0.0.0.0:8200"
|
||||
tls_disable = 1
|
||||
tls_cert_file = "work/ca/certs/localhost.cert.pem"
|
||||
tls_key_file = "work/ca/private/localhost.decrypted.key.pem"
|
||||
}
|
||||
|
||||
disable_mlock = true
|
||||
|
||||
Reference in New Issue
Block a user