diff --git a/.gitignore b/.gitignore
index 57c5e02c..2990b124 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ _site/
download/
vault/download
vault/vault
+work
diff --git a/.travis.yml b/.travis.yml
index a0cf4ab0..342b20be 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -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 &
diff --git a/README.adoc b/README.adoc
index 35e27ab0..7a1c04ad 100644
--- a/README.adoc
+++ b/README.adoc
@@ -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
diff --git a/docs/src/main/asciidoc/README.adoc b/docs/src/main/asciidoc/README.adoc
index 340c670f..2ff8dee4 100644
--- a/docs/src/main/asciidoc/README.adoc
+++ b/docs/src/main/asciidoc/README.adoc
@@ -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
diff --git a/docs/src/main/asciidoc/spring-cloud-vault-config.adoc b/docs/src/main/asciidoc/spring-cloud-vault-config.adoc
index d8eca96b..b363630f 100644
--- a/docs/src/main/asciidoc/spring-cloud-vault-config.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-vault-config.adoc
@@ -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.
+
diff --git a/pom.xml b/pom.xml
index 84a140f0..c41bfacd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,6 +22,10 @@
UTF-8
1.7
+ 4.5.2
+ 4.4.4
+ 4.1.0.Final
+ 2.7.5
@@ -54,6 +58,41 @@
spring-boot-starter-jdbc
test
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${httpclient.version}
+ true
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+
+ org.apache.httpcomponents
+ httpcore
+ ${httpcore.version}
+ true
+
+
+
+ io.netty
+ netty-all
+ ${netty.version}
+ true
+
+
+
+ com.squareup.okhttp
+ okhttp
+ ${okhttp.version}
+ true
+
+
com.h2database
h2
@@ -66,9 +105,9 @@
test
- mysql
- mysql-connector-java
- 5.1.38
+ mysql
+ mysql-connector-java
+ 5.1.38
test
diff --git a/src/main/java/org/springframework/cloud/vault/ClientHttpRequestFactoryFactory.java b/src/main/java/org/springframework/cloud/vault/ClientHttpRequestFactoryFactory.java
new file mode 100644
index 00000000..1d6f183e
--- /dev/null
+++ b/src/main/java/org/springframework/cloud/vault/ClientHttpRequestFactoryFactory.java
@@ -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;
+ }
+}
diff --git a/src/main/java/org/springframework/cloud/vault/VaultBootstrapConfiguration.java b/src/main/java/org/springframework/cloud/vault/VaultBootstrapConfiguration.java
index 39ac35fe..f0a94927 100644
--- a/src/main/java/org/springframework/cloud/vault/VaultBootstrapConfiguration.java
+++ b/src/main/java/org/springframework/cloud/vault/VaultBootstrapConfiguration.java
@@ -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 appIdUserIdMechanisms = applicationContext
.getBeansOfType(AppIdUserIdMechanism.class);
diff --git a/src/main/java/org/springframework/cloud/vault/VaultClient.java b/src/main/java/org/springframework/cloud/vault/VaultClient.java
index 0fe16d85..c71fa657 100644
--- a/src/main/java/org/springframework/cloud/vault/VaultClient.java
+++ b/src/main/java/org/springframework/cloud/vault/VaultClient.java
@@ -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 read(SecureBackendAccessor secureBackendAccessor, VaultToken vaultToken) {
+ public VaultClient(VaultProperties properties) {
+
+ Assert.notNull(properties, "VaultProperties must not be null");
+
+ this.properties = properties;
+ }
+
+ public Map 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 response = this.rest.exchange(url,
- HttpMethod.GET, new HttpEntity<>(headers), VaultResponse.class,
- secureBackendAccessor.variables());
+ ResponseEntity 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 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;
}
diff --git a/src/main/java/org/springframework/cloud/vault/VaultProperties.java b/src/main/java/org/springframework/cloud/vault/VaultProperties.java
index c7ecdecd..860fa94d 100644
--- a/src/main/java/org/springframework/cloud/vault/VaultProperties.java
+++ b/src/main/java/org/springframework/cloud/vault/VaultProperties.java
@@ -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 {
diff --git a/src/test/bash/create_certificates.sh b/src/test/bash/create_certificates.sh
new file mode 100755
index 00000000..2181ab41
--- /dev/null
+++ b/src/test/bash/create_certificates.sh
@@ -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
diff --git a/src/test/bash/env.sh b/src/test/bash/env.sh
index 1cf1fb9a..274302d5 100755
--- a/src/test/bash/env.sh
+++ b/src/test/bash/env.sh
@@ -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
diff --git a/src/test/bash/openssl.cnf b/src/test/bash/openssl.cnf
new file mode 100644
index 00000000..fdf9064d
--- /dev/null
+++ b/src/test/bash/openssl.cnf
@@ -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 .
+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
\ No newline at end of file
diff --git a/src/test/java/org/springframework/cloud/vault/AppIdAuthenticationIntegrationTests.java b/src/test/java/org/springframework/cloud/vault/AppIdAuthenticationIntegrationTests.java
index 1666772c..d4346542 100644
--- a/src/test/java/org/springframework/cloud/vault/AppIdAuthenticationIntegrationTests.java
+++ b/src/test/java/org/springframework/cloud/vault/AppIdAuthenticationIntegrationTests.java
@@ -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
diff --git a/src/test/java/org/springframework/cloud/vault/AppIdAuthenticationMethodsIntegrationTests.java b/src/test/java/org/springframework/cloud/vault/AppIdAuthenticationMethodsIntegrationTests.java
index e9cbc33c..1248e475 100644
--- a/src/test/java/org/springframework/cloud/vault/AppIdAuthenticationMethodsIntegrationTests.java
+++ b/src/test/java/org/springframework/cloud/vault/AppIdAuthenticationMethodsIntegrationTests.java
@@ -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)
diff --git a/src/test/java/org/springframework/cloud/vault/CassandraSecretIntegrationTests.java b/src/test/java/org/springframework/cloud/vault/CassandraSecretIntegrationTests.java
index 9855b694..4fc872d2 100644
--- a/src/test/java/org/springframework/cloud/vault/CassandraSecretIntegrationTests.java
+++ b/src/test/java/org/springframework/cloud/vault/CassandraSecretIntegrationTests.java
@@ -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
diff --git a/src/test/java/org/springframework/cloud/vault/ClientHttpRequestFactoryFactoryIntegrationTests.java b/src/test/java/org/springframework/cloud/vault/ClientHttpRequestFactoryFactoryIntegrationTests.java
new file mode 100644
index 00000000..5a985b9c
--- /dev/null
+++ b/src/test/java/org/springframework/cloud/vault/ClientHttpRequestFactoryFactoryIntegrationTests.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/springframework/cloud/vault/GenericSecretIntegrationTests.java b/src/test/java/org/springframework/cloud/vault/GenericSecretIntegrationTests.java
index 31dcf714..a49effcb 100644
--- a/src/test/java/org/springframework/cloud/vault/GenericSecretIntegrationTests.java
+++ b/src/test/java/org/springframework/cloud/vault/GenericSecretIntegrationTests.java
@@ -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 secretProperties = vaultClient
- .read(generic(vaultProperties, "missing"), createToken());
-
- assertThat(secretProperties).isNull();
+ vaultClient.read(generic(vaultProperties, "missing"), createToken());
}
/**
diff --git a/src/test/java/org/springframework/cloud/vault/MySqlSecretIntegrationTests.java b/src/test/java/org/springframework/cloud/vault/MySqlSecretIntegrationTests.java
index f97564fa..6f5d7117 100644
--- a/src/test/java/org/springframework/cloud/vault/MySqlSecretIntegrationTests.java
+++ b/src/test/java/org/springframework/cloud/vault/MySqlSecretIntegrationTests.java
@@ -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
diff --git a/src/test/java/org/springframework/cloud/vault/PostgreSqlSecretIntegrationTests.java b/src/test/java/org/springframework/cloud/vault/PostgreSqlSecretIntegrationTests.java
index a1c308f6..58eddb99 100644
--- a/src/test/java/org/springframework/cloud/vault/PostgreSqlSecretIntegrationTests.java
+++ b/src/test/java/org/springframework/cloud/vault/PostgreSqlSecretIntegrationTests.java
@@ -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
diff --git a/src/test/java/org/springframework/cloud/vault/PrepareVaultTests.java b/src/test/java/org/springframework/cloud/vault/PrepareVaultTests.java
index def8056d..51d8aed1 100644
--- a/src/test/java/org/springframework/cloud/vault/PrepareVaultTests.java
+++ b/src/test/java/org/springframework/cloud/vault/PrepareVaultTests.java
@@ -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 {
diff --git a/src/test/java/org/springframework/cloud/vault/TestRestTemplateFactory.java b/src/test/java/org/springframework/cloud/vault/TestRestTemplateFactory.java
new file mode 100644
index 00000000..4d7c66bf
--- /dev/null
+++ b/src/test/java/org/springframework/cloud/vault/TestRestTemplateFactory.java
@@ -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 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();
+ }
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/src/test/java/org/springframework/cloud/vault/util/Settings.java b/src/test/java/org/springframework/cloud/vault/util/Settings.java
index f62db9e9..b8503a95 100644
--- a/src/test/java/org/springframework/cloud/vault/util/Settings.java
+++ b/src/test/java/org/springframework/cloud/vault/util/Settings.java
@@ -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;
}
diff --git a/src/test/java/org/springframework/cloud/vault/util/VaultRule.java b/src/test/java/org/springframework/cloud/vault/util/VaultRule.java
index 3c66a92f..7e719c68 100644
--- a/src/test/java/org/springframework/cloud/vault/util/VaultRule.java
+++ b/src/test/java/org/springframework/cloud/vault/util/VaultRule.java
@@ -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
diff --git a/src/test/resources/bootstrap.yml b/src/test/resources/bootstrap.yml
index 0cafc06b..05b0ca87 100644
--- a/src/test/resources/bootstrap.yml
+++ b/src/test/resources/bootstrap.yml
@@ -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
diff --git a/src/test/resources/vault.conf b/src/test/resources/vault.conf
index b30cf3ff..bbb4510e 100644
--- a/src/test/resources/vault.conf
+++ b/src/test/resources/vault.conf
@@ -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