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