From 49aee4cf51f35f945a523bd0552c4a2cdcf3a8d4 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 13 Jan 2020 12:08:15 +0100 Subject: [PATCH] Allow disabling authentication for Vault Agent use We now allow disabling Spring Vault's ClientAuthentication and SessionManagement features to rely on authenticated requests sent by Vault Agent. The authentication infrastructure can be disabled via: spring.cloud.vault: authentication: NONE Closes gh-356. --- .../src/main/asciidoc/spring-cloud-vault.adoc | 26 ++++++++- pom.xml | 1 + .../config/ConditionalOnAuthentication.java | 41 ++++++++++++++ .../config/VaultBootstrapConfiguration.java | 16 +++++- .../cloud/vault/config/VaultProperties.java | 2 +- .../VaultReactiveBootstrapConfiguration.java | 22 ++++++-- ...ctiveVaultBootstrapConfigurationTests.java | 56 +++++++++++++++++++ .../VaultBootstrapConfigurationTests.java | 51 +++++++++++++++++ spring-cloud-vault-dependencies/pom.xml | 2 +- 9 files changed, 206 insertions(+), 11 deletions(-) create mode 100644 spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/ConditionalOnAuthentication.java create mode 100644 spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/ReactiveVaultBootstrapConfigurationTests.java create mode 100644 spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultBootstrapConfigurationTests.java diff --git a/docs/src/main/asciidoc/spring-cloud-vault.adoc b/docs/src/main/asciidoc/spring-cloud-vault.adoc index 05aeeb7e..61399d0f 100644 --- a/docs/src/main/asciidoc/spring-cloud-vault.adoc +++ b/docs/src/main/asciidoc/spring-cloud-vault.adoc @@ -6,7 +6,7 @@ :toc: left :nofooter: -(C) 2016-2019 The original authors. +(C) 2016-2020 The original authors. NOTE: _Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically._ @@ -50,6 +50,30 @@ authentication method See also: https://www.vaultproject.io/docs/concepts/tokens.html[Vault Documentation: Tokens] +[[vault.config.authentication.vault-agent]] +=== Vault Agent authentication + +Vault ships a sidecar utility with Vault Agent since version 0.11.0. +Vault Agent implements the functionality of Spring Vault's `SessionManager` +with its Auto-Auth feature. Applications can reuse cached session credentials by relying +on Vault Agent running on `localhost`. Spring Vault can send requests without the +`X-Vault-Token` header. Disable Spring Vault's authentication infrastructure to +disable client authentication and session management. + +.bootstrap.yml +==== +[source,yaml] +---- +spring.cloud.vault: + authentication: NONE +---- +==== + +* `authentication` setting this value to `NONE` disables `ClientAuthentication` +and `SessionManager`. + +See also: https://www.vaultproject.io/docs/agent/index.html[Vault Documentation: Agent] + [[vault.config.authentication.appid]] === AppId authentication diff --git a/pom.xml b/pom.xml index d61867de..1ac4329a 100644 --- a/pom.xml +++ b/pom.xml @@ -188,6 +188,7 @@ org.codehaus.mojo flatten-maven-plugin + 1.1.0 org.apache.maven.plugins diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/ConditionalOnAuthentication.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/ConditionalOnAuthentication.java new file mode 100644 index 00000000..d2766402 --- /dev/null +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/ConditionalOnAuthentication.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2020 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 + * + * https://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.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.annotation.Conditional; + +/** + * {@link Conditional @Conditional} that only matches when no authentication is configured + * (i.e. {@code spring.cloud.vault.authentication=NONE}). + * + * @author Mark Paluch + * @since 2.2.2 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@ConditionalOnExpression("!'NONE'.equalsIgnoreCase('${spring.cloud.vault.authentication}')") +@interface ConditionalOnAuthentication { + +} diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultBootstrapConfiguration.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultBootstrapConfiguration.java index 88094767..6d88f575 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultBootstrapConfiguration.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultBootstrapConfiguration.java @@ -165,14 +165,22 @@ public class VaultBootstrapConfiguration implements InitializingBean { /** * Creates a {@link VaultTemplate}. - * @param sessionManager the {@link SessionManager}. * @return the {@link VaultTemplate} bean. * @see VaultBootstrapConfiguration#clientHttpRequestFactoryWrapper() */ @Bean @ConditionalOnMissingBean(VaultOperations.class) - public VaultTemplate vaultTemplate(SessionManager sessionManager) { - return new VaultTemplate(this.restTemplateBuilder, sessionManager); + public VaultTemplate vaultTemplate() { + + VaultProperties.AuthenticationMethod authentication = this.vaultProperties + .getAuthentication(); + + if (authentication == VaultProperties.AuthenticationMethod.NONE) { + return new VaultTemplate(this.restTemplateBuilder); + } + + return new VaultTemplate(this.restTemplateBuilder, + this.applicationContext.getBean(SessionManager.class)); } /** @@ -208,6 +216,7 @@ public class VaultBootstrapConfiguration implements InitializingBean { */ @Bean @ConditionalOnMissingBean + @ConditionalOnAuthentication public SessionManager vaultSessionManager(ClientAuthentication clientAuthentication, ObjectFactory asyncTaskExecutorFactory) { @@ -229,6 +238,7 @@ public class VaultBootstrapConfiguration implements InitializingBean { */ @Bean @ConditionalOnMissingBean + @ConditionalOnAuthentication public ClientAuthentication clientAuthentication() { RestTemplate restTemplate = this.restTemplateBuilder.build(); diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultProperties.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultProperties.java index 1494ac9c..16e04da8 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultProperties.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultProperties.java @@ -336,7 +336,7 @@ public class VaultProperties implements EnvironmentAware { */ public enum AuthenticationMethod { - APPID, APPROLE, AWS_EC2, AWS_IAM, AZURE_MSI, CERT, CUBBYHOLE, GCP_GCE, GCP_IAM, KUBERNETES, PCF, TOKEN; + APPID, APPROLE, AWS_EC2, AWS_IAM, AZURE_MSI, CERT, CUBBYHOLE, GCP_GCE, GCP_IAM, KUBERNETES, NONE, PCF, TOKEN; } diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultReactiveBootstrapConfiguration.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultReactiveBootstrapConfiguration.java index 61823d54..02652116 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultReactiveBootstrapConfiguration.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultReactiveBootstrapConfiguration.java @@ -85,6 +85,8 @@ import org.springframework.web.reactive.function.client.WebClient; @Order(Ordered.LOWEST_PRECEDENCE - 10) public class VaultReactiveBootstrapConfiguration { + private final BeanFactory beanFactory; + private final VaultProperties vaultProperties; /** @@ -92,10 +94,12 @@ public class VaultReactiveBootstrapConfiguration { */ private final WebClientBuilder webClientBuilder; - public VaultReactiveBootstrapConfiguration(VaultProperties vaultProperties, + public VaultReactiveBootstrapConfiguration(BeanFactory beanFactory, + VaultProperties vaultProperties, ObjectProvider endpointProvider, ObjectProvider> webClientCustomizers) { + this.beanFactory = beanFactory; this.vaultProperties = vaultProperties; VaultEndpointProvider provider = endpointProvider.getIfAvailable(); @@ -141,15 +145,20 @@ public class VaultReactiveBootstrapConfiguration { /** * Creates a {@link ReactiveVaultTemplate}. - * @param tokenSupplier the {@link VaultTokenSupplier}. * @return the {@link ReactiveVaultTemplate} bean. * @see #reactiveVaultSessionManager(BeanFactory, ObjectFactory) */ @Bean @ConditionalOnMissingBean(ReactiveVaultOperations.class) - public ReactiveVaultTemplate reactiveVaultTemplate( - ReactiveSessionManager tokenSupplier) { - return new ReactiveVaultTemplate(this.webClientBuilder, tokenSupplier); + public ReactiveVaultTemplate reactiveVaultTemplate() { + + if (this.vaultProperties + .getAuthentication() == VaultProperties.AuthenticationMethod.NONE) { + return new ReactiveVaultTemplate(this.webClientBuilder); + } + + return new ReactiveVaultTemplate(this.webClientBuilder, + beanFactory.getBean(ReactiveSessionManager.class)); } /** @@ -162,6 +171,7 @@ public class VaultReactiveBootstrapConfiguration { */ @Bean @ConditionalOnMissingBean + @ConditionalOnAuthentication public ReactiveSessionManager reactiveVaultSessionManager(BeanFactory beanFactory, ObjectFactory asyncTaskExecutorFactory) { @@ -184,6 +194,7 @@ public class VaultReactiveBootstrapConfiguration { */ @Bean @ConditionalOnMissingBean + @ConditionalOnAuthentication public SessionManager vaultSessionManager(ReactiveSessionManager sessionManager) { return sessionManager.getSessionToken()::block; } @@ -197,6 +208,7 @@ public class VaultReactiveBootstrapConfiguration { */ @Bean @ConditionalOnMissingBean(name = "vaultTokenSupplier") + @ConditionalOnAuthentication public VaultTokenSupplier vaultTokenSupplier(ListableBeanFactory beanFactory) { Assert.notNull(beanFactory, "BeanFactory must not be null"); diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/ReactiveVaultBootstrapConfigurationTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/ReactiveVaultBootstrapConfigurationTests.java new file mode 100644 index 00000000..05ce5294 --- /dev/null +++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/ReactiveVaultBootstrapConfigurationTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2018-2020 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 + * + * https://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.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.authentication.ReactiveSessionManager; +import org.springframework.vault.authentication.SessionManager; +import org.springframework.vault.authentication.VaultTokenSupplier; +import org.springframework.vault.core.ReactiveVaultTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit tests for {@link VaultReactiveBootstrapConfiguration}. + * + * @author Mark Paluch + */ +public class ReactiveVaultBootstrapConfigurationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(VaultBootstrapConfiguration.class, + VaultReactiveBootstrapConfiguration.class)); + + @Test + public void shouldConfigureWithoutAuthentication() { + + this.contextRunner.withPropertyValues("spring.cloud.vault.generic.enabled=false", + "spring.cloud.vault.authentication=NONE").run(context -> { + + assertThat(context).doesNotHaveBean(SessionManager.class); + assertThat(context).doesNotHaveBean(ClientAuthentication.class); + assertThat(context).doesNotHaveBean(VaultTokenSupplier.class); + assertThat(context).doesNotHaveBean(ReactiveSessionManager.class); + assertThat(context).hasSingleBean(ReactiveVaultTemplate.class); + }); + } + +} diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultBootstrapConfigurationTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultBootstrapConfigurationTests.java new file mode 100644 index 00000000..7b7335a6 --- /dev/null +++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultBootstrapConfigurationTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2018-2020 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 + * + * https://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.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.authentication.SessionManager; +import org.springframework.vault.core.VaultTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit tests for {@link VaultBootstrapConfiguration}. + * + * @author Mark Paluch + */ +public class VaultBootstrapConfigurationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(VaultBootstrapConfiguration.class)); + + @Test + public void shouldConfigureWithoutAuthentication() { + + this.contextRunner.withPropertyValues("spring.cloud.vault.generic.enabled=false", + "spring.cloud.vault.authentication=NONE").run(context -> { + + assertThat(context).doesNotHaveBean(SessionManager.class); + assertThat(context).doesNotHaveBean(ClientAuthentication.class); + assertThat(context).hasSingleBean(VaultTemplate.class); + }); + } + +} diff --git a/spring-cloud-vault-dependencies/pom.xml b/spring-cloud-vault-dependencies/pom.xml index d5f2f49e..e9026622 100644 --- a/spring-cloud-vault-dependencies/pom.xml +++ b/spring-cloud-vault-dependencies/pom.xml @@ -20,7 +20,7 @@ Spring Cloud Vault Dependencies - 2.2.0.RELEASE + 2.2.1.BUILD-SNAPSHOT