diff --git a/docs/src/main/asciidoc/spring-cloud-vault.adoc b/docs/src/main/asciidoc/spring-cloud-vault.adoc index 99a8c09b..1fd3bc0c 100644 --- a/docs/src/main/asciidoc/spring-cloud-vault.adoc +++ b/docs/src/main/asciidoc/spring-cloud-vault.adoc @@ -670,15 +670,17 @@ JDBC secrets need to be configured separately. [[vault.config.backends.database]] === Database -Spring Cloud Vault can obtain credentials for any Database listed at +Spring Cloud Vault can obtain credentials for any database listed at https://www.vaultproject.io/api/secret/databases/index.html. The integration can be enabled by setting `spring.cloud.vault.database.enabled=true` (default `false`) and providing the role name with `spring.cloud.vault.database.role=…`. -Username and password are stored in `spring.datasource.username` -and `spring.datasource.password` so using Spring Boot will -pick up the generated credentials without further configuration. +While the database backend is a generic one, `spring.cloud.vault.database` +specifically targets JDBC databases. Username and password are +stored in `spring.datasource.username` and `spring.datasource.password` +so using Spring Boot will pick up the generated credentials +for your `DataSource` without further configuration. You can configure the property names by setting `spring.cloud.vault.database.username-property` and `spring.cloud.vault.database.password-property`. @@ -704,11 +706,11 @@ spring.cloud.vault: See also: https://www.vaultproject.io/docs/secrets/databases/index.html[Vault Documentation: Database Secrets backend] - [[vault.config.backends.cassandra]] === Apache Cassandra -This backend has been deprecated in Vault and it is recommended to use the `database` backend -and mount it as `cassandra`. + +NOTE: The `cassandra` backend has been deprecated in Vault 0.7.1 and +it is recommended to use the `database` backend and mount it as `cassandra`. Spring Cloud Vault can obtain credentials for Apache Cassandra. The integration can be enabled by setting @@ -746,8 +748,8 @@ See also: https://www.vaultproject.io/docs/secrets/cassandra/index.html[Vault Do [[vault.config.backends.mongodb]] === MongoDB -This backend has been deprecated in Vault and it is recommended to use the `database` backend -and mount it as `mongodb`. +NOTE: The `mongodb` backend has been deprecated in Vault 0.7.1 and +it is recommended to use the `database` backend and mount it as `mongodb`. Spring Cloud Vault can obtain credentials for MongoDB. The integration can be enabled by setting @@ -784,8 +786,10 @@ See also: https://www.vaultproject.io/docs/secrets/mongodb/index.html[Vault Docu [[vault.config.backends.mysql]] === MySQL -This backend has been deprecated in Vault and it is recommended to use the `database` backend -and mount it as `mysql`. + +NOTE: The `mysql` backend has been deprecated in Vault 0.7.1 and +it is recommended to use the `database` backend and mount it as `mysql`. +Configuration for `spring.cloud.vault.mysql` will be removed in a future version. Spring Cloud Vault can obtain credentials for MySQL. The integration can be enabled by setting @@ -822,8 +826,10 @@ See also: https://www.vaultproject.io/docs/secrets/mysql/index.html[Vault Docume [[vault.config.backends.postgresql]] === PostgreSQL -This backend has been deprecated in Vault and it is recommended to use the `database` backend -and mount it as `postgresql`. + +NOTE: The `postgresql` backend has been deprecated in Vault 0.7.1 and +it is recommended to use the `database` backend and mount it as `postgresql`. +Configuration for `spring.cloud.vault.postgresql` will be removed in a future version. Spring Cloud Vault can obtain credentials for PostgreSQL. The integration can be enabled by setting diff --git a/pom.xml b/pom.xml index 21ffc97d..d69fd54c 100644 --- a/pom.xml +++ b/pom.xml @@ -144,6 +144,7 @@ org.projectlombok lombok provided + 1.16.18 true diff --git a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultConfigDatabaseBootstrapConfiguration.java b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultConfigDatabaseBootstrapConfiguration.java index 9095ba8f..6daa8501 100644 --- a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultConfigDatabaseBootstrapConfiguration.java +++ b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultConfigDatabaseBootstrapConfiguration.java @@ -31,9 +31,10 @@ import org.springframework.vault.core.util.PropertyTransformer; /** * Bootstrap configuration providing support for the Database secret backends such as - * MySQL, PostreSQL, Apache Cassandra and MongoDB. + * Database, Apache Cassandra and MongoDB. * * @author Mark Paluch + * @author Per Abich */ @Configuration @EnableConfigurationProperties({ VaultMySqlProperties.class, @@ -51,8 +52,8 @@ public class VaultConfigDatabaseBootstrapConfiguration { * {@link SecretBackendMetadataFactory} for Database integration using * {@link DatabaseSecretProperties}. */ - public static class DatabaseSecretBackendMetadataFactory - implements SecretBackendMetadataFactory { + public static class DatabaseSecretBackendMetadataFactory implements + SecretBackendMetadataFactory { @Override public SecretBackendMetadata createMetadata( @@ -75,16 +76,15 @@ public class VaultConfigDatabaseBootstrapConfiguration { * @param properties must not be {@literal null}. * @return the {@link SecretBackendMetadata} */ - static SecretBackendMetadata forDatabase( - final DatabaseSecretProperties properties) { + static SecretBackendMetadata forDatabase(final DatabaseSecretProperties properties) { Assert.notNull(properties, "DatabaseSecretProperties must not be null"); final PropertyNameTransformer transformer = new PropertyNameTransformer(); - transformer.addKeyTransformation("username", - properties.getUsernameProperty()); - transformer.addKeyTransformation("password", - properties.getPasswordProperty()); + transformer + .addKeyTransformation("username", properties.getUsernameProperty()); + transformer + .addKeyTransformation("password", properties.getPasswordProperty()); return new SecretBackendMetadata() { diff --git a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultDatabaseProperties.java b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultDatabaseProperties.java index c9e45300..3967b7cd 100644 --- a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultDatabaseProperties.java +++ b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultDatabaseProperties.java @@ -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,47 +15,49 @@ */ package org.springframework.cloud.vault.config.databases; +import javax.validation.constraints.NotEmpty; + import lombok.Data; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.validation.annotation.Validated; -import javax.validation.constraints.NotEmpty; - /** * Configuration properties for Vault using the Database integration. * * @author Per Abich + * @since 2.0 */ @ConfigurationProperties("spring.cloud.vault.database") @Data @Validated public class VaultDatabaseProperties implements DatabaseSecretProperties { - /** - * Enable database backend usage. - */ - private boolean enabled = false; + /** + * Enable database backend usage. + */ + private boolean enabled = false; - /** - * Role name for credentials. - */ - private String role; + /** + * Role name for credentials. + */ + private String role; - /** - * Database backend path. - */ - @NotEmpty - private String backend = "database"; + /** + * Database backend path. + */ + @NotEmpty + private String backend = "database"; - /** - * Target property for the obtained username. - */ - @NotEmpty - private String usernameProperty = "spring.datasource.username"; + /** + * Target property for the obtained username. + */ + @NotEmpty + private String usernameProperty = "spring.datasource.username"; - /** - * Target property for the obtained password. - */ - @NotEmpty - private String passwordProperty = "spring.datasource.password"; + /** + * Target property for the obtained password. + */ + @NotEmpty + private String passwordProperty = "spring.datasource.password"; } diff --git a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultMySqlProperties.java b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultMySqlProperties.java index 9aa179cb..7df3248d 100644 --- a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultMySqlProperties.java +++ b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultMySqlProperties.java @@ -18,8 +18,10 @@ package org.springframework.cloud.vault.config.databases; import javax.validation.constraints.NotEmpty; import lombok.Data; +import lombok.Getter; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; import org.springframework.validation.annotation.Validated; @@ -27,21 +29,25 @@ import org.springframework.validation.annotation.Validated; * Configuration properties for Vault using the MySQL integration. * * @author Mark Paluch + * @deprecated since 2.0. Use {@link VaultDatabaseProperties}. */ @ConfigurationProperties("spring.cloud.vault.mysql") @Data @Validated +@Deprecated public class VaultMySqlProperties implements DatabaseSecretProperties, VaultSecretBackendDescriptor { /** * Enable mysql backend usage. */ + @Getter(onMethod_ = { @DeprecatedConfigurationProperty(reason = "Use spring.cloud.vault.database") }) private boolean enabled = false; /** * Role name for credentials. */ + @Getter(onMethod_ = { @DeprecatedConfigurationProperty(reason = "Use spring.cloud.vault.database") }) private String role; /** diff --git a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultPostgreSqlProperties.java b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultPostgreSqlProperties.java index 99695981..2aec587a 100644 --- a/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultPostgreSqlProperties.java +++ b/spring-cloud-vault-config-databases/src/main/java/org/springframework/cloud/vault/config/databases/VaultPostgreSqlProperties.java @@ -15,30 +15,38 @@ */ package org.springframework.cloud.vault.config.databases; -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - import javax.validation.constraints.NotEmpty; +import lombok.Data; +import lombok.Getter; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; +import org.springframework.validation.annotation.Validated; + /** * Configuration properties for Vault using the PostgreSQL integration. * * @author Mark Paluch + * @deprecated since 2.0. Use {@link VaultDatabaseProperties}. */ @ConfigurationProperties("spring.cloud.vault.postgresql") @Data @Validated +@Deprecated public class VaultPostgreSqlProperties implements DatabaseSecretProperties { /** * Enable postgresql backend usage. */ + @Deprecated + @Getter(onMethod_ = { @DeprecatedConfigurationProperty(reason = "Use spring.cloud.vault.database") }) private boolean enabled = false; /** * Role name for credentials. */ + @Getter(onMethod_ = { @DeprecatedConfigurationProperty(reason = "Use spring.cloud.vault.database") }) private String role; /** diff --git a/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/VaultConfigMySqlDatabaseTests.java b/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/VaultConfigMySqlDatabaseTests.java new file mode 100644 index 00000000..51dc5923 --- /dev/null +++ b/spring-cloud-vault-config-databases/src/test/java/org/springframework/cloud/vault/config/databases/VaultConfigMySqlDatabaseTests.java @@ -0,0 +1,129 @@ +/* + * 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.databases; + +import java.net.InetSocketAddress; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import javax.sql.DataSource; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.vault.util.CanConnect; +import org.springframework.cloud.vault.util.VaultRule; +import org.springframework.cloud.vault.util.Version; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.vault.core.VaultOperations; + +import static org.junit.Assume.assumeTrue; + +/** + * Integration tests using the database secret backend. In case this test should fail + * because of SSL make sure you run the test within the + * spring-cloud-vault-config/spring-cloud-vault-config directory as the keystore is + * referenced with {@code ../work/keystore.jks}. + * + * @author Mark Paluch + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = VaultConfigMySqlDatabaseTests.TestApplication.class, properties = { + "spring.cloud.vault.database.enabled=true", + "spring.cloud.vault.database.role=readonly", + "spring.datasource.url=jdbc:mysql://localhost:3306/mysql?useSSL=false" }) +public class VaultConfigMySqlDatabaseTests { + + private final static int MYSQL_PORT = 3306; + private final static String MYSQL_HOST = "localhost"; + private final static String ROOT_CREDENTIALS = String.format( + "springvault:springvault@tcp(%s:%d)/", MYSQL_HOST, MYSQL_PORT); + private final static String CREATE_USER_AND_GRANT_SQL = "CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';" + + "GRANT SELECT ON *.* TO '{{name}}'@'%';"; + + /** + * Initialize the mysql secret backend. + * + * @throws Exception + */ + @BeforeClass + public static void beforeClass() throws Exception { + + VaultRule vaultRule = new VaultRule(); + vaultRule.before(); + + assumeTrue(CanConnect.to(new InetSocketAddress(MYSQL_HOST, MYSQL_PORT))); + assumeTrue(vaultRule.prepare().getVersion() + .isGreaterThanOrEqualTo(Version.parse("0.7.1"))); + + if (!vaultRule.prepare().hasSecretBackend("database")) { + vaultRule.prepare().mountSecret("database"); + } + + VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations(); + + Map config = new HashMap<>(); + config.put("plugin_name", "mysql-legacy-database-plugin"); + config.put("connection_url", ROOT_CREDENTIALS); + config.put("allowed_roles", "readonly"); + + vaultOperations.write("database/config/mysql", config); + + Map body = new HashMap<>(); + body.put("db_name", "mysql"); + body.put("creation_statements", CREATE_USER_AND_GRANT_SQL); + + vaultOperations.write("database/roles/readonly", body); + } + + @Value("${spring.datasource.username}") + String username; + + @Value("${spring.datasource.password}") + String password; + + @Autowired + DataSource dataSource; + + @Test + public void shouldConnectUsingDataSource() throws SQLException { + + dataSource.getConnection().close(); + } + + @Test + public void shouldConnectUsingJdbcUrlConnection() throws SQLException { + + String url = String.format("jdbc:mysql://%s?useSSL=false", MYSQL_HOST); + DriverManager.getConnection(url, username, password).close(); + } + + @SpringBootApplication + public static class TestApplication { + + public static void main(String[] args) { + SpringApplication.run(TestApplication.class, args); + } + } +}