Split configuration into parts.
Closes gh-135.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user