Split configuration into parts.

Closes gh-135.
This commit is contained in:
Mark Paluch
2017-07-13 20:55:32 +02:00
parent 68c2eb94b6
commit d6c6295cfa
6 changed files with 445 additions and 279 deletions

View File

@@ -0,0 +1,170 @@
/*
* Copyright 2017 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.config;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.vault.authentication.*;
import org.springframework.vault.authentication.AwsEc2AuthenticationOptions.Nonce;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.RestOperations;
/**
* Factory for {@link ClientAuthentication}.
*
* @author Mark Paluch
* @since 1.1
*/
@RequiredArgsConstructor
class ClientAuthenticationFactory {
private final VaultProperties vaultProperties;
private final RestOperations restOperations;
/**
* @return a new {@link ClientAuthentication}.
*/
public ClientAuthentication createClientAuthentication() {
switch (vaultProperties.getAuthentication()) {
case TOKEN:
Assert.hasText(vaultProperties.getToken(),
"Token (spring.cloud.vault.token) must not be empty");
return new TokenAuthentication(vaultProperties.getToken());
case APPID:
return appIdAuthentication(vaultProperties);
case APPROLE:
return appRoleAuthentication(vaultProperties);
case CERT:
return new ClientCertificateAuthentication(restOperations);
case AWS_EC2:
return awsEc2Authentication(vaultProperties);
case CUBBYHOLE:
return cubbyholeAuthentication();
}
throw new UnsupportedOperationException(String.format(
"Client authentication %s not supported",
vaultProperties.getAuthentication()));
}
private ClientAuthentication appIdAuthentication(VaultProperties vaultProperties) {
VaultProperties.AppIdProperties appId = vaultProperties.getAppId();
Assert.hasText(appId.getUserId(),
"UserId (spring.cloud.vault.app-id.user-id) must not be empty");
AppIdAuthenticationOptions authenticationOptions = AppIdAuthenticationOptions
.builder().appId(vaultProperties.getApplicationName()) //
.path(appId.getAppIdPath()) //
.userIdMechanism(getClientAuthentication(appId)).build();
return new AppIdAuthentication(authenticationOptions, restOperations);
}
private AppIdUserIdMechanism getClientAuthentication(
VaultProperties.AppIdProperties appId) {
try {
Class<?> userIdClass = ClassUtils.forName(appId.getUserId(), null);
return (AppIdUserIdMechanism) BeanUtils.instantiateClass(userIdClass);
}
catch (ClassNotFoundException ex) {
switch (appId.getUserId().toUpperCase()) {
case VaultProperties.AppIdProperties.IP_ADDRESS:
return new IpAddressUserId();
case VaultProperties.AppIdProperties.MAC_ADDRESS:
if (StringUtils.hasText(appId.getNetworkInterface())) {
try {
return new MacAddressUserId(Integer.parseInt(appId
.getNetworkInterface()));
}
catch (NumberFormatException e) {
return new MacAddressUserId(appId.getNetworkInterface());
}
}
return new MacAddressUserId();
default:
return new StaticUserId(appId.getUserId());
}
}
}
private ClientAuthentication appRoleAuthentication(VaultProperties vaultProperties) {
VaultProperties.AppRoleProperties appRole = vaultProperties.getAppRole();
Assert.hasText(appRole.getRoleId(),
"RoleId (spring.cloud.vault.app-role.role-id) must not be empty");
AppRoleAuthenticationOptions.AppRoleAuthenticationOptionsBuilder builder = AppRoleAuthenticationOptions
.builder().path(appRole.getAppRolePath()).roleId(appRole.getRoleId());
if (StringUtils.hasText(appRole.getSecretId())) {
builder = builder.secretId(appRole.getSecretId());
}
return new AppRoleAuthentication(builder.build(), restOperations);
}
private ClientAuthentication awsEc2Authentication(VaultProperties vaultProperties) {
VaultProperties.AwsEc2Properties awsEc2 = vaultProperties.getAwsEc2();
Nonce nonce = StringUtils.hasText(awsEc2.getNonce()) ? Nonce.provided(awsEc2
.getNonce().toCharArray()) : Nonce.generated();
AwsEc2AuthenticationOptions authenticationOptions = AwsEc2AuthenticationOptions
.builder().role(awsEc2.getRole()) //
.path(awsEc2.getAwsEc2Path()) //
.nonce(nonce) //
.identityDocumentUri(URI.create(awsEc2.getIdentityDocument())) //
.build();
return new AwsEc2Authentication(authenticationOptions, restOperations,
restOperations);
}
private ClientAuthentication cubbyholeAuthentication() {
Assert.hasText(vaultProperties.getToken(),
"Initial Token (spring.cloud.vault.token) for Cubbyhole authentication must not be empty");
CubbyholeAuthenticationOptions options = CubbyholeAuthenticationOptions.builder() //
.wrapped() //
.initialToken(VaultToken.of(vaultProperties.getToken())) //
.build();
return new CubbyholeAuthentication(options, restOperations);
}
}

View File

@@ -15,15 +15,9 @@
*/
package org.springframework.cloud.vault.config;
import static org.springframework.cloud.vault.config.GenericSecretBackendMetadata.create;
import java.net.URI;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectFactory;
@@ -31,7 +25,6 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -42,39 +35,19 @@ 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.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.vault.authentication.AppIdAuthentication;
import org.springframework.vault.authentication.AppIdAuthenticationOptions;
import org.springframework.vault.authentication.AppIdUserIdMechanism;
import org.springframework.vault.authentication.AppRoleAuthentication;
import org.springframework.vault.authentication.AppRoleAuthenticationOptions;
import org.springframework.vault.authentication.AwsEc2Authentication;
import org.springframework.vault.authentication.AwsEc2AuthenticationOptions;
import org.springframework.vault.authentication.AwsEc2AuthenticationOptions.Nonce;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.ClientCertificateAuthentication;
import org.springframework.vault.authentication.CubbyholeAuthentication;
import org.springframework.vault.authentication.CubbyholeAuthenticationOptions;
import org.springframework.vault.authentication.IpAddressUserId;
import org.springframework.vault.authentication.LifecycleAwareSessionManager;
import org.springframework.vault.authentication.MacAddressUserId;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.authentication.SimpleSessionManager;
import org.springframework.vault.authentication.StaticUserId;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.VaultClients;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.config.AbstractVaultConfiguration.ClientFactoryWrapper;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.config.AbstractVaultConfiguration.ClientFactoryWrapper;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.lease.SecretLeaseContainer;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.vault.support.SslConfiguration.KeyStoreConfiguration;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.RestOperations;
/**
@@ -85,8 +58,8 @@ import org.springframework.web.client.RestOperations;
*/
@Configuration
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
@EnableConfigurationProperties({ VaultProperties.class, VaultGenericBackendProperties.class })
@Order(Ordered.LOWEST_PRECEDENCE - 10)
@EnableConfigurationProperties(VaultProperties.class)
@Order(Ordered.LOWEST_PRECEDENCE - 5)
public class VaultBootstrapConfiguration implements InitializingBean {
private final ConfigurableApplicationContext applicationContext;
@@ -97,10 +70,6 @@ public class VaultBootstrapConfiguration implements InitializingBean {
private RestOperations restOperations;
private Collection<VaultSecretBackendDescriptor> vaultSecretBackendDescriptors;
private Collection<SecretBackendMetadataFactory<? super VaultSecretBackendDescriptor>> factories;
public VaultBootstrapConfiguration(ConfigurableApplicationContext applicationContext,
VaultProperties vaultProperties) {
@@ -125,104 +94,32 @@ public class VaultBootstrapConfiguration implements InitializingBean {
@Override
@SuppressWarnings("unchecked")
public void afterPropertiesSet() throws Exception {
public void afterPropertiesSet() {
this.vaultSecretBackendDescriptors = applicationContext.getBeansOfType(VaultSecretBackendDescriptor.class).values();
ClientHttpRequestFactory clientHttpRequestFactory = clientHttpRequestFactoryWrapper()
.getClientHttpRequestFactory();
this.factories = (Collection) applicationContext.getBeansOfType(SecretBackendMetadataFactory.class).values();
ClientHttpRequestFactory clientHttpRequestFactory = clientHttpRequestFactoryWrapper().getClientHttpRequestFactory();
this.restOperations = VaultClients.createRestTemplate(vaultEndpoint, clientHttpRequestFactory);
}
@Bean
public PropertySourceLocator vaultPropertySourceLocator(VaultOperations operations, VaultProperties vaultProperties,
VaultGenericBackendProperties vaultGenericBackendProperties,
ObjectFactory<SecretLeaseContainer> secretLeaseContainerObjectFactory) {
VaultConfigTemplate vaultConfigTemplate = new VaultConfigTemplate(operations, vaultProperties);
PropertySourceLocatorConfiguration propertySourceLocatorConfiguration = getPropertySourceConfiguration(
vaultGenericBackendProperties);
if (vaultProperties.getConfig().getLifecycle().isEnabled()) {
// This is to destroy bootstrap resources
// otherwise, the bootstrap context is not shut down cleanly
applicationContext.registerShutdownHook();
SecretLeaseContainer secretLeaseContainer = secretLeaseContainerObjectFactory.getObject();
secretLeaseContainer.start();
return new LeasingVaultPropertySourceLocator(vaultProperties, propertySourceLocatorConfiguration,
secretLeaseContainer);
}
return new VaultPropertySourceLocator(vaultConfigTemplate, vaultProperties, propertySourceLocatorConfiguration);
}
private PropertySourceLocatorConfiguration getPropertySourceConfiguration(
VaultGenericBackendProperties vaultGenericBackendProperties) {
Collection<VaultConfigurer> configurers = applicationContext.getBeansOfType(VaultConfigurer.class).values();
DefaultSecretBackendConfigurer secretBackendConfigurer = new DefaultSecretBackendConfigurer();
if (configurers.isEmpty()) {
secretBackendConfigurer.registerDefaultGenericSecretBackends(true).registerDefaultDiscoveredSecretBackends(true);
} else {
for (VaultConfigurer vaultConfigurer : configurers) {
vaultConfigurer.addSecretBackends(secretBackendConfigurer);
}
}
if (secretBackendConfigurer.isRegisterDefaultGenericSecretBackends()) {
if (vaultGenericBackendProperties.isEnabled()) {
List<String> contexts = GenericSecretBackendMetadata.buildContexts(vaultGenericBackendProperties,
Arrays.asList(applicationContext.getEnvironment().getActiveProfiles()));
for (String context : contexts) {
secretBackendConfigurer.add(create(vaultGenericBackendProperties.getBackend(), context));
}
}
Collection<SecretBackendMetadata> backendAccessors = SecretBackendFactories
.createSecretBackendMetadata(vaultSecretBackendDescriptors, factories);
for (SecretBackendMetadata metadata : backendAccessors) {
secretBackendConfigurer.add(metadata);
}
}
if (secretBackendConfigurer.isRegisterDefaultDiscoveredSecretBackends()) {
Collection<SecretBackendMetadata> backendAccessors = SecretBackendFactories
.createSecretBackendMetadata(vaultSecretBackendDescriptors, factories);
for (SecretBackendMetadata metadata : backendAccessors) {
secretBackendConfigurer.add(metadata);
}
}
return secretBackendConfigurer;
this.restOperations = VaultClients.createRestTemplate(vaultEndpoint,
clientHttpRequestFactory);
}
/**
* Creates a {@link ClientFactoryWrapper} containing a {@link ClientHttpRequestFactory}.
* {@link ClientHttpRequestFactory} is not exposed as root bean because {@link ClientHttpRequestFactory} is configured
* with {@link ClientOptions} and {@link SslConfiguration} which are not necessarily applicable for the whole
* application.
* Creates a {@link ClientFactoryWrapper} containing a
* {@link ClientHttpRequestFactory}. {@link ClientHttpRequestFactory} is not exposed
* as root bean because {@link ClientHttpRequestFactory} is configured with
* {@link ClientOptions} and {@link SslConfiguration} which are not necessarily
* applicable for the whole application.
*
* @return the {@link ClientFactoryWrapper} to wrap a {@link ClientHttpRequestFactory} instance.
* @return the {@link ClientFactoryWrapper} to wrap a {@link ClientHttpRequestFactory}
* instance.
*/
@Bean
@ConditionalOnMissingBean
public ClientFactoryWrapper clientHttpRequestFactoryWrapper() {
ClientOptions clientOptions = new ClientOptions(Duration.ofMillis(vaultProperties.getConnectionTimeout()),
Duration.ofMillis(vaultProperties.getReadTimeout()));
ClientOptions clientOptions = new ClientOptions(Duration.ofMillis(vaultProperties
.getConnectionTimeout()), Duration.ofMillis(vaultProperties
.getReadTimeout()));
VaultProperties.Ssl ssl = vaultProperties.getSsl();
SslConfiguration sslConfiguration;
@@ -233,20 +130,24 @@ public class VaultBootstrapConfiguration implements InitializingBean {
if (ssl.getKeyStore() != null) {
keyStore = new KeyStoreConfiguration(ssl.getKeyStore(),
ssl.getKeyStorePassword() != null ? ssl.getKeyStorePassword().toCharArray() : null, null);
ssl.getKeyStorePassword() != null ? ssl.getKeyStorePassword()
.toCharArray() : null, null);
}
if (ssl.getTrustStore() != null) {
trustStore = new KeyStoreConfiguration(ssl.getTrustStore(),
ssl.getTrustStorePassword() != null ? ssl.getTrustStorePassword().toCharArray() : null, null);
ssl.getTrustStorePassword() != null ? ssl.getTrustStorePassword()
.toCharArray() : null, null);
}
sslConfiguration = new SslConfiguration(keyStore, trustStore);
} else {
}
else {
sslConfiguration = SslConfiguration.NONE;
}
return new ClientFactoryWrapper(ClientHttpRequestFactoryFactory.create(clientOptions, sslConfiguration));
return new ClientFactoryWrapper(ClientHttpRequestFactoryFactory.create(
clientOptions, sslConfiguration));
}
/**
@@ -258,13 +159,13 @@ public class VaultBootstrapConfiguration implements InitializingBean {
@Bean
@ConditionalOnMissingBean
public VaultTemplate vaultTemplate(SessionManager sessionManager) {
return new VaultTemplate(vaultEndpoint, clientHttpRequestFactoryWrapper().getClientHttpRequestFactory(),
sessionManager);
return new VaultTemplate(vaultEndpoint, clientHttpRequestFactoryWrapper()
.getClientHttpRequestFactory(), sessionManager);
}
/**
* Creates a new {@link TaskSchedulerWrapper} that encapsulates a bean implementing {@link TaskScheduler} and
* {@link AsyncTaskExecutor}.
* Creates a new {@link TaskSchedulerWrapper} that encapsulates a bean implementing
* {@link TaskScheduler} and {@link AsyncTaskExecutor}.
*
* @return
* @see ThreadPoolTaskScheduler
@@ -298,140 +199,27 @@ public class VaultBootstrapConfiguration implements InitializingBean {
if (vaultProperties.getConfig().getLifecycle().isEnabled()) {
return new LifecycleAwareSessionManager(clientAuthentication,
asyncTaskExecutorFactory.getObject().getTaskScheduler(), restOperations);
asyncTaskExecutorFactory.getObject().getTaskScheduler(),
restOperations);
}
return new SimpleSessionManager(clientAuthentication);
}
/**
* @return the {@link SessionManager} for Vault session management.
* @return the {@link ClientAuthentication} to obtain a
* {@link org.springframework.vault.support.VaultToken}.
* @see SessionManager
* @see LifecycleAwareSessionManager
*/
@Bean
@Lazy
@ConditionalOnMissingBean
public SecretLeaseContainer secretLeaseContainer(VaultOperations vaultOperations,
TaskSchedulerWrapper taskSchedulerWrapper) {
return new SecretLeaseContainer(vaultOperations, taskSchedulerWrapper.getTaskScheduler());
}
@Bean
@ConditionalOnMissingBean
public ClientAuthentication clientAuthentication() {
switch (vaultProperties.getAuthentication()) {
ClientAuthenticationFactory factory = new ClientAuthenticationFactory(
vaultProperties, restOperations);
case TOKEN:
Assert.hasText(vaultProperties.getToken(), "Token (spring.cloud.vault.token) must not be empty");
return new TokenAuthentication(vaultProperties.getToken());
case APPID:
return appIdAuthentication(vaultProperties);
case APPROLE:
return appRoleAuthentication(vaultProperties);
case CERT:
return new ClientCertificateAuthentication(restOperations);
case AWS_EC2:
return awsEc2Authentication(vaultProperties);
case CUBBYHOLE:
return cubbyholeAuthentication();
}
throw new UnsupportedOperationException(
String.format("Client authentication %s not supported", vaultProperties.getAuthentication()));
}
private ClientAuthentication appIdAuthentication(VaultProperties vaultProperties) {
VaultProperties.AppIdProperties appId = vaultProperties.getAppId();
Assert.hasText(appId.getUserId(), "UserId (spring.cloud.vault.app-id.user-id) must not be empty");
AppIdAuthenticationOptions authenticationOptions = AppIdAuthenticationOptions.builder()
.appId(vaultProperties.getApplicationName()) //
.path(appId.getAppIdPath()) //
.userIdMechanism(getClientAuthentication(appId)).build();
return new AppIdAuthentication(authenticationOptions, restOperations);
}
private AppIdUserIdMechanism getClientAuthentication(VaultProperties.AppIdProperties appId) {
try {
Class<?> userIdClass = ClassUtils.forName(appId.getUserId(), null);
return (AppIdUserIdMechanism) BeanUtils.instantiateClass(userIdClass);
} catch (ClassNotFoundException ex) {
switch (appId.getUserId().toUpperCase()) {
case VaultProperties.AppIdProperties.IP_ADDRESS:
return new IpAddressUserId();
case VaultProperties.AppIdProperties.MAC_ADDRESS:
if (StringUtils.hasText(appId.getNetworkInterface())) {
try {
return new MacAddressUserId(Integer.parseInt(appId.getNetworkInterface()));
} catch (NumberFormatException e) {
return new MacAddressUserId(appId.getNetworkInterface());
}
}
return new MacAddressUserId();
default:
return new StaticUserId(appId.getUserId());
}
}
}
private ClientAuthentication appRoleAuthentication(VaultProperties vaultProperties) {
VaultProperties.AppRoleProperties appRole = vaultProperties.getAppRole();
Assert.hasText(appRole.getRoleId(), "RoleId (spring.cloud.vault.app-role.role-id) must not be empty");
AppRoleAuthenticationOptions.AppRoleAuthenticationOptionsBuilder builder = AppRoleAuthenticationOptions.builder()
.path(appRole.getAppRolePath()).roleId(appRole.getRoleId());
if (StringUtils.hasText(appRole.getSecretId())) {
builder = builder.secretId(appRole.getSecretId());
}
return new AppRoleAuthentication(builder.build(), restOperations);
}
private ClientAuthentication awsEc2Authentication(VaultProperties vaultProperties) {
VaultProperties.AwsEc2Properties awsEc2 = vaultProperties.getAwsEc2();
Nonce nonce = StringUtils.hasText(awsEc2.getNonce()) ? Nonce.provided(awsEc2.getNonce().toCharArray())
: Nonce.generated();
AwsEc2AuthenticationOptions authenticationOptions = AwsEc2AuthenticationOptions.builder().role(awsEc2.getRole()) //
.path(awsEc2.getAwsEc2Path()) //
.nonce(nonce) //
.identityDocumentUri(URI.create(awsEc2.getIdentityDocument())) //
.build();
return new AwsEc2Authentication(authenticationOptions, restOperations, restOperations);
}
private ClientAuthentication cubbyholeAuthentication() {
Assert.hasText(vaultProperties.getToken(),
"Initial Token (spring.cloud.vault.token) for Cubbyhole authentication must not be empty");
CubbyholeAuthenticationOptions options = CubbyholeAuthenticationOptions.builder() //
.wrapped() //
.initialToken(VaultToken.of(vaultProperties.getToken())) //
.build();
return new CubbyholeAuthentication(options, restOperations);
return factory.createClientAuthentication();
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2017 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.
@@ -15,35 +15,15 @@
*/
package org.springframework.cloud.vault.config;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.vault.core.VaultOperations;
/**
* @author Stuart Ingram
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} for Vault providing beans for the application context.
*
* @author Mark Paluch
* @author Stuart Ingram
* @deprecated since 1.1, use {@link VaultHealthIndicatorConfiguration}.
*/
@Configuration
@ConditionalOnBean(VaultBootstrapConfiguration.class)
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
@ConditionalOnExpression("${health.vault.enabled:true}")
@AutoConfigureBefore({ EndpointAutoConfiguration.class })
@AutoConfigureAfter({ VaultBootstrapConfiguration.class,
HealthIndicatorAutoConfiguration.class })
public class VaultBootstrapHealthIndicatorConfiguration {
@Bean
@ConditionalOnMissingBean(name = "vaultHealthIndicator")
public HealthIndicator vaultHealthIndicator(VaultOperations vaultOperations) {
return new VaultHealthIndicator(vaultOperations);
}
@Deprecated
public class VaultBootstrapHealthIndicatorConfiguration extends
VaultHealthIndicatorConfiguration {
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright 2017 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.config;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.cloud.vault.config.VaultBootstrapConfiguration.TaskSchedulerWrapper;
import org.springframework.context.ConfigurableApplicationContext;
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.Order;
import org.springframework.vault.authentication.LifecycleAwareSessionManager;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.lease.SecretLeaseContainer;
import static org.springframework.cloud.vault.config.GenericSecretBackendMetadata.*;
/**
* {@link org.springframework.cloud.bootstrap.BootstrapConfiguration Auto-configuration}
* for Spring Vault's {@link PropertySourceLocator} support.
*
* @author Mark Paluch
* @since 1.1
*/
@Configuration
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
@EnableConfigurationProperties(VaultGenericBackendProperties.class)
@Order(Ordered.LOWEST_PRECEDENCE - 10)
public class VaultBootstrapPropertySourceConfiguration implements InitializingBean {
private final ConfigurableApplicationContext applicationContext;
private Collection<VaultSecretBackendDescriptor> vaultSecretBackendDescriptors;
private Collection<SecretBackendMetadataFactory<? super VaultSecretBackendDescriptor>> factories;
public VaultBootstrapPropertySourceConfiguration(
ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
@SuppressWarnings("unchecked")
public void afterPropertiesSet() throws Exception {
this.vaultSecretBackendDescriptors = applicationContext.getBeansOfType(
VaultSecretBackendDescriptor.class).values();
this.factories = (Collection) applicationContext.getBeansOfType(
SecretBackendMetadataFactory.class).values();
}
@Bean
public PropertySourceLocator vaultPropertySourceLocator(VaultOperations operations,
VaultProperties vaultProperties,
VaultGenericBackendProperties vaultGenericBackendProperties,
ObjectFactory<SecretLeaseContainer> secretLeaseContainerObjectFactory) {
VaultConfigTemplate vaultConfigTemplate = new VaultConfigTemplate(operations,
vaultProperties);
PropertySourceLocatorConfiguration propertySourceLocatorConfiguration = getPropertySourceConfiguration(vaultGenericBackendProperties);
if (vaultProperties.getConfig().getLifecycle().isEnabled()) {
// This is to destroy bootstrap resources
// otherwise, the bootstrap context is not shut down cleanly
applicationContext.registerShutdownHook();
SecretLeaseContainer secretLeaseContainer = secretLeaseContainerObjectFactory
.getObject();
secretLeaseContainer.start();
return new LeasingVaultPropertySourceLocator(vaultProperties,
propertySourceLocatorConfiguration, secretLeaseContainer);
}
return new VaultPropertySourceLocator(vaultConfigTemplate, vaultProperties,
propertySourceLocatorConfiguration);
}
private PropertySourceLocatorConfiguration getPropertySourceConfiguration(
VaultGenericBackendProperties vaultGenericBackendProperties) {
Collection<VaultConfigurer> configurers = applicationContext.getBeansOfType(
VaultConfigurer.class).values();
DefaultSecretBackendConfigurer secretBackendConfigurer = new DefaultSecretBackendConfigurer();
if (configurers.isEmpty()) {
secretBackendConfigurer.registerDefaultGenericSecretBackends(true)
.registerDefaultDiscoveredSecretBackends(true);
}
else {
for (VaultConfigurer vaultConfigurer : configurers) {
vaultConfigurer.addSecretBackends(secretBackendConfigurer);
}
}
if (secretBackendConfigurer.isRegisterDefaultGenericSecretBackends()) {
if (vaultGenericBackendProperties.isEnabled()) {
List<String> contexts = GenericSecretBackendMetadata.buildContexts(
vaultGenericBackendProperties, Arrays.asList(applicationContext
.getEnvironment().getActiveProfiles()));
for (String context : contexts) {
secretBackendConfigurer.add(create(
vaultGenericBackendProperties.getBackend(), context));
}
}
Collection<SecretBackendMetadata> backendAccessors = SecretBackendFactories
.createSecretBackendMetadata(vaultSecretBackendDescriptors, factories);
for (SecretBackendMetadata metadata : backendAccessors) {
secretBackendConfigurer.add(metadata);
}
}
if (secretBackendConfigurer.isRegisterDefaultDiscoveredSecretBackends()) {
Collection<SecretBackendMetadata> backendAccessors = SecretBackendFactories
.createSecretBackendMetadata(vaultSecretBackendDescriptors, factories);
for (SecretBackendMetadata metadata : backendAccessors) {
secretBackendConfigurer.add(metadata);
}
}
return secretBackendConfigurer;
}
/**
* @return the {@link SessionManager} for Vault session management.
* @see SessionManager
* @see LifecycleAwareSessionManager
*/
@Bean
@Lazy
@ConditionalOnMissingBean
public SecretLeaseContainer secretLeaseContainer(VaultOperations vaultOperations,
TaskSchedulerWrapper taskSchedulerWrapper) {
return new SecretLeaseContainer(vaultOperations,
taskSchedulerWrapper.getTaskScheduler());
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.config;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.vault.core.VaultOperations;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} for Vault providing beans for the application context.
*
* @author Stuart Ingram
* @author Mark Paluch
* @since 1.1
*/
@Configuration
@ConditionalOnClass(HealthIndicator.class)
@ConditionalOnBean(VaultBootstrapConfiguration.class)
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
@ConditionalOnExpression("${health.vault.enabled:true}")
@AutoConfigureBefore({ EndpointAutoConfiguration.class })
@AutoConfigureAfter(HealthIndicatorAutoConfiguration.class)
public class VaultHealthIndicatorConfiguration {
@Bean
@ConditionalOnMissingBean(name = "vaultHealthIndicator")
public HealthIndicator vaultHealthIndicator(VaultOperations vaultOperations) {
return new VaultHealthIndicator(vaultOperations);
}
}

View File

@@ -1,5 +1,8 @@
# Auto-Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.vault.config.VaultHealthIndicatorConfiguration
# Bootstrap Configuration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.vault.config.VaultBootstrapConfiguration,\
org.springframework.cloud.vault.config.ReactiveVaultBootstrapConfiguration,\
org.springframework.cloud.vault.config.VaultBootstrapHealthIndicatorConfiguration
org.springframework.cloud.vault.config.VaultBootstrapPropertySourceConfiguration