Add X-Vault-Namespace Support.
We now support Vault namespaces by setting the spring.cloud.vault.namespace property. Closes gh-259
This commit is contained in:
@@ -1291,6 +1291,28 @@ spring.cloud.vault:
|
||||
----
|
||||
====
|
||||
|
||||
[[vault.config.namespaces]]
|
||||
== Vault Enterprise Namespace Support
|
||||
|
||||
Vault Enterprise allows using namespaces to isolate multiple Vaults
|
||||
on a single Vault server. Configuring a namespace by setting
|
||||
`spring.cloud.vault.namespace=…` enables the namespace header
|
||||
`X-Vault-Namespace` on every outgoing HTTP request when using the Vault
|
||||
`RestTemplate` or `WebClient`.
|
||||
|
||||
Please note that this feature is not supported by Vault Community edition
|
||||
and has no effect on Vault operations.
|
||||
|
||||
====
|
||||
[source,yaml]
|
||||
----
|
||||
spring.cloud.vault:
|
||||
namespace: my-namespace
|
||||
----
|
||||
====
|
||||
|
||||
See also: https://www.vaultproject.io/docs/enterprise/namespaces/index.html[Vault Enterprise: Namespaces]
|
||||
|
||||
[[vault.config.ssl]]
|
||||
== Vault Client SSL configuration
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -31,20 +34,25 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.authentication.ClientAuthentication;
|
||||
import org.springframework.vault.authentication.LifecycleAwareSessionManager;
|
||||
import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.authentication.SimpleSessionManager;
|
||||
import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
|
||||
import org.springframework.vault.client.RestTemplateBuilder;
|
||||
import org.springframework.vault.client.RestTemplateCustomizer;
|
||||
import org.springframework.vault.client.RestTemplateRequestCustomizer;
|
||||
import org.springframework.vault.client.SimpleVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultClients;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultHttpHeaders;
|
||||
import org.springframework.vault.config.AbstractVaultConfiguration.ClientFactoryWrapper;
|
||||
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
import org.springframework.vault.support.ClientOptions;
|
||||
@@ -70,10 +78,14 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
|
||||
private final VaultEndpointProvider endpointProvider;
|
||||
|
||||
private final List<RestTemplateCustomizer> customizers;
|
||||
|
||||
private final List<RestTemplateRequestCustomizer<?>> requestCustomizers;
|
||||
|
||||
/**
|
||||
* Used for Vault communication.
|
||||
*/
|
||||
private RestOperations restOperations;
|
||||
private RestTemplateBuilder restTemplateBuilder;
|
||||
|
||||
/**
|
||||
* Used for external (AWS, GCP) communication.
|
||||
@@ -82,7 +94,9 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
|
||||
public VaultBootstrapConfiguration(ConfigurableApplicationContext applicationContext,
|
||||
VaultProperties vaultProperties,
|
||||
ObjectProvider<VaultEndpointProvider> endpointProvider) {
|
||||
ObjectProvider<VaultEndpointProvider> endpointProvider,
|
||||
ObjectProvider<List<RestTemplateCustomizer>> customizers,
|
||||
ObjectProvider<List<RestTemplateRequestCustomizer<?>>> requestCustomizers) {
|
||||
|
||||
this.applicationContext = applicationContext;
|
||||
this.vaultProperties = vaultProperties;
|
||||
@@ -95,6 +109,13 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
}
|
||||
|
||||
this.endpointProvider = provider;
|
||||
this.customizers = new ArrayList<>(
|
||||
customizers.getIfAvailable(Collections::emptyList));
|
||||
AnnotationAwareOrderComparator.sort(this.customizers);
|
||||
|
||||
this.requestCustomizers = new ArrayList<>(
|
||||
requestCustomizers.getIfAvailable(Collections::emptyList));
|
||||
AnnotationAwareOrderComparator.sort(this.requestCustomizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,8 +124,17 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
ClientHttpRequestFactory clientHttpRequestFactory = clientHttpRequestFactoryWrapper()
|
||||
.getClientHttpRequestFactory();
|
||||
|
||||
this.restOperations = VaultClients.createRestTemplate(this.endpointProvider,
|
||||
clientHttpRequestFactory);
|
||||
this.restTemplateBuilder = RestTemplateBuilder.builder()
|
||||
.requestFactory(clientHttpRequestFactory)
|
||||
.endpointProvider(this.endpointProvider);
|
||||
|
||||
this.customizers.forEach(this.restTemplateBuilder::customizers);
|
||||
this.requestCustomizers.forEach(this.restTemplateBuilder::requestCustomizers);
|
||||
|
||||
if (StringUtils.hasText(this.vaultProperties.getNamespace())) {
|
||||
this.restTemplateBuilder.defaultHeader(VaultHttpHeaders.VAULT_NAMESPACE,
|
||||
this.vaultProperties.getNamespace());
|
||||
}
|
||||
|
||||
this.externalRestOperations = new RestTemplate(clientHttpRequestFactory);
|
||||
}
|
||||
@@ -142,9 +172,7 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(VaultOperations.class)
|
||||
public VaultTemplate vaultTemplate(SessionManager sessionManager) {
|
||||
return new VaultTemplate(this.endpointProvider,
|
||||
clientHttpRequestFactoryWrapper().getClientHttpRequestFactory(),
|
||||
sessionManager);
|
||||
return new VaultTemplate(this.restTemplateBuilder, sessionManager);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,9 +212,10 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
ObjectFactory<TaskSchedulerWrapper> asyncTaskExecutorFactory) {
|
||||
|
||||
if (this.vaultProperties.getConfig().getLifecycle().isEnabled()) {
|
||||
RestTemplate restTemplate = this.restTemplateBuilder.build();
|
||||
return new LifecycleAwareSessionManager(clientAuthentication,
|
||||
asyncTaskExecutorFactory.getObject().getTaskScheduler(),
|
||||
this.restOperations);
|
||||
restTemplate);
|
||||
}
|
||||
|
||||
return new SimpleSessionManager(clientAuthentication);
|
||||
@@ -202,8 +231,9 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
@ConditionalOnMissingBean
|
||||
public ClientAuthentication clientAuthentication() {
|
||||
|
||||
RestTemplate restTemplate = this.restTemplateBuilder.build();
|
||||
ClientAuthenticationFactory factory = new ClientAuthenticationFactory(
|
||||
this.vaultProperties, this.restOperations, this.externalRestOperations);
|
||||
this.vaultProperties, restTemplate, this.externalRestOperations);
|
||||
|
||||
return factory.createClientAuthentication();
|
||||
}
|
||||
|
||||
@@ -70,6 +70,11 @@ public class VaultProperties implements EnvironmentAware {
|
||||
*/
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* Vault namespace (requires Vault Enterprise).
|
||||
*/
|
||||
private String namespace;
|
||||
|
||||
/**
|
||||
* Discovery properties.
|
||||
*/
|
||||
@@ -154,6 +159,10 @@ public class VaultProperties implements EnvironmentAware {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return this.namespace;
|
||||
}
|
||||
|
||||
public Discovery getDiscovery() {
|
||||
return this.discovery;
|
||||
}
|
||||
@@ -246,6 +255,10 @@ public class VaultProperties implements EnvironmentAware {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public void setDiscovery(Discovery discovery) {
|
||||
this.discovery = discovery;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -36,10 +39,12 @@ import org.springframework.cloud.vault.config.VaultBootstrapConfiguration.TaskSc
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.authentication.AuthenticationStepsFactory;
|
||||
import org.springframework.vault.authentication.AuthenticationStepsOperator;
|
||||
import org.springframework.vault.authentication.CachingVaultTokenSupplier;
|
||||
@@ -49,10 +54,12 @@ import org.springframework.vault.authentication.ReactiveSessionManager;
|
||||
import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.authentication.VaultTokenSupplier;
|
||||
import org.springframework.vault.client.ReactiveVaultClients;
|
||||
import org.springframework.vault.client.ClientHttpConnectorFactory;
|
||||
import org.springframework.vault.client.SimpleVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.config.ClientHttpConnectorFactory;
|
||||
import org.springframework.vault.client.VaultHttpHeaders;
|
||||
import org.springframework.vault.client.WebClientBuilder;
|
||||
import org.springframework.vault.client.WebClientCustomizer;
|
||||
import org.springframework.vault.core.ReactiveVaultOperations;
|
||||
import org.springframework.vault.core.ReactiveVaultTemplate;
|
||||
import org.springframework.vault.support.ClientOptions;
|
||||
@@ -80,12 +87,14 @@ public class VaultReactiveBootstrapConfiguration {
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
|
||||
private final VaultEndpointProvider endpointProvider;
|
||||
|
||||
private final ClientHttpConnector clientHttpConnector;
|
||||
/**
|
||||
* Used for Vault communication.
|
||||
*/
|
||||
private final WebClientBuilder webClientBuilder;
|
||||
|
||||
public VaultReactiveBootstrapConfiguration(VaultProperties vaultProperties,
|
||||
ObjectProvider<VaultEndpointProvider> endpointProvider) {
|
||||
ObjectProvider<VaultEndpointProvider> endpointProvider,
|
||||
ObjectProvider<List<WebClientCustomizer>> webClientCustomizers) {
|
||||
|
||||
this.vaultProperties = vaultProperties;
|
||||
|
||||
@@ -96,9 +105,19 @@ public class VaultReactiveBootstrapConfiguration {
|
||||
.of(VaultConfigurationUtil.createVaultEndpoint(vaultProperties));
|
||||
}
|
||||
|
||||
this.endpointProvider = provider;
|
||||
this.webClientBuilder = WebClientBuilder.builder()
|
||||
.httpConnector(createConnector(this.vaultProperties))
|
||||
.endpointProvider(provider);
|
||||
List<WebClientCustomizer> customizers = new ArrayList<>(
|
||||
webClientCustomizers.getIfAvailable(Collections::emptyList));
|
||||
AnnotationAwareOrderComparator.sort(customizers);
|
||||
|
||||
this.clientHttpConnector = createConnector(this.vaultProperties);
|
||||
customizers.forEach(this.webClientBuilder::customizers);
|
||||
|
||||
if (StringUtils.hasText(this.vaultProperties.getNamespace())) {
|
||||
this.webClientBuilder.defaultHeader(VaultHttpHeaders.VAULT_NAMESPACE,
|
||||
this.vaultProperties.getNamespace());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,8 +149,7 @@ public class VaultReactiveBootstrapConfiguration {
|
||||
@ConditionalOnMissingBean(ReactiveVaultOperations.class)
|
||||
public ReactiveVaultTemplate reactiveVaultTemplate(
|
||||
ReactiveSessionManager tokenSupplier) {
|
||||
return new ReactiveVaultTemplate(this.endpointProvider, this.clientHttpConnector,
|
||||
tokenSupplier);
|
||||
return new ReactiveVaultTemplate(this.webClientBuilder, tokenSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,8 +170,7 @@ public class VaultReactiveBootstrapConfiguration {
|
||||
|
||||
if (this.vaultProperties.getConfig().getLifecycle().isEnabled()) {
|
||||
|
||||
WebClient webClient = ReactiveVaultClients
|
||||
.createWebClient(this.endpointProvider, this.clientHttpConnector);
|
||||
WebClient webClient = this.webClientBuilder.build();
|
||||
return new ReactiveLifecycleAwareSessionManager(vaultTokenSupplier,
|
||||
asyncTaskExecutorFactory.getObject().getTaskScheduler(), webClient);
|
||||
}
|
||||
@@ -225,8 +242,7 @@ public class VaultReactiveBootstrapConfiguration {
|
||||
|
||||
private VaultTokenSupplier createAuthenticationStepsOperator(
|
||||
AuthenticationStepsFactory factory) {
|
||||
WebClient webClient = ReactiveVaultClients.createWebClient(this.endpointProvider,
|
||||
this.clientHttpConnector);
|
||||
WebClient webClient = this.webClientBuilder.build();
|
||||
return new AuthenticationStepsOperator(factory.getAuthenticationSteps(),
|
||||
webClient);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user