Support MongoDB credential generation.

Fixes gh-28.
This commit is contained in:
Mark Paluch
2016-09-25 17:50:33 +02:00
parent 7a6dc4d8b4
commit dbfdb93ad9
11 changed files with 356 additions and 23 deletions

View File

@@ -4,6 +4,15 @@ services:
- mysql
- postgresql
- rabbitmq
- mongodb
addons:
apt:
sources:
- mongodb-3.0-precise
packages:
- mongodb-org-server
- mongodb-org-shell
jdk:
- oraclejdk8
@@ -26,6 +35,8 @@ before_script:
- mysql -e "CREATE USER 'spring' IDENTIFIED by 'vault';"
- mysql -uroot -e "GRANT ALL PRIVILEGES ON *.* TO 'spring'@'%' WITH GRANT OPTION;";
- psql -U postgres -c "CREATE ROLE spring WITH LOGIN PASSWORD 'vault' CREATEROLE CREATEUSER;"
- |-
mongo admin --eval "db.createUser({user: 'spring', pwd:'vault', roles:['root']});"
- sleep 30 # wait until Cassandra is up
- apache-cassandra-2.2.6/bin/cqlsh localhost -u cassandra -p cassandra -e "CREATE USER 'spring' WITH PASSWORD 'vault' SUPERUSER"

View File

@@ -1,7 +1,7 @@
// Do not edit this file (e.g. go instead to docs/src/main/asciidoc)
Spring Cloud Vault Config provides client-side support for externalized configuration in a distributed system. With https://www.vaultproject.io[Hashicorp's Vault] you have a central place to manage external secret properties for applications across all environments. Vault can manage static and dynamic secrets such as username/password for remote applications/resources and provide credentials for external services such as MySQL, PostgreSQL, Apache Cassandra, Consul, AWS and more.
Spring Cloud Vault Config provides client-side support for externalized configuration in a distributed system. With https://www.vaultproject.io[Hashicorp's Vault] you have a central place to manage external secret properties for applications across all environments. Vault can manage static and dynamic secrets such as username/password for remote applications/resources and provide credentials for external services such as MySQL, PostgreSQL, Apache Cassandra, MongoDB, Consul, AWS and more.
== Features
@@ -9,8 +9,9 @@ Spring Cloud Vault Config provides client-side support for externalized configur
Specifically for Spring applications:
* Bind to the Config Server and initialize Spring `Environment` with remote property sources
* Retrieve secrets from Vault and initialize Spring `Environment` with remote property sources
* Obtain secrets secured with SSL
* Generate credentials for MySQL, PostgreSQL, Apache Cassandra, MongoDB, Consul, AWS, and RabbitMQ.
* https://www.vaultproject.io/docs/auth/token.html[Token], https://www.vaultproject.io/docs/auth/app-id.html[AppId] authentication,
and https://www.vaultproject.io/docs/auth/aws-ec2.html[AWS-EC2] authentication
* Bootstrap application context: a parent context for the main application that can be trained to do anything
@@ -37,7 +38,7 @@ $ src/test/bash/install_vault.sh
$ src/test/bash/create_certificates.sh
----
NOTE: `create_certificates.sh` creates certificates in `work/ca` and a JKS truststore `work/keystore.jsk`. If you want to run Spring Cloud Vault using this quickstart guide you need to configure the truststore the `spring.cloud.vault.ssl.trust-store` property to `file:work/keystore.jks`.
NOTE: `create_certificates.sh` creates certificates in `work/ca` and a JKS truststore `work/keystore.jks`. If you want to run Spring Cloud Vault using this quickstart guide you need to configure the truststore the `spring.cloud.vault.ssl.trust-store` property to `file:work/keystore.jks`.
*Start Vault server*

View File

@@ -7,8 +7,9 @@ include::intro.adoc[]
Specifically for Spring applications:
* Bind to the Config Server and initialize Spring `Environment` with remote property sources
* Retrieve secrets from Vault and initialize Spring `Environment` with remote property sources
* Obtain secrets secured with SSL
* Generate credentials for MySQL, PostgreSQL, Apache Cassandra, MongoDB, Consul, AWS, and RabbitMQ.
* https://www.vaultproject.io/docs/auth/token.html[Token], https://www.vaultproject.io/docs/auth/app-id.html[AppId] authentication,
and https://www.vaultproject.io/docs/auth/aws-ec2.html[AWS-EC2] authentication
* Bootstrap application context: a parent context for the main application that can be trained to do anything

View File

@@ -1,2 +1,2 @@
Spring Cloud Vault Config provides client-side support for externalized configuration in a distributed system. With https://www.vaultproject.io[Hashicorp's Vault] you have a central place to manage external secret properties for applications across all environments. Vault can manage static and dynamic secrets such as username/password for remote applications/resources and provide credentials for external services such as MySQL, PostgreSQL, Apache Cassandra, Consul, AWS and more.
Spring Cloud Vault Config provides client-side support for externalized configuration in a distributed system. With https://www.vaultproject.io[Hashicorp's Vault] you have a central place to manage external secret properties for applications across all environments. Vault can manage static and dynamic secrets such as username/password for remote applications/resources and provide credentials for external services such as MySQL, PostgreSQL, Apache Cassandra, MongoDB, Consul, AWS and more.

View File

@@ -490,6 +490,7 @@ mechanism to more easily roll keys.
Spring Cloud Vault integrates with these backends:
* <<vault-client-database-cassandra>>
* <<vault-client-database-mongodb>>
* <<vault-client-database-mysql>>
* <<vault-client-database-postgresql>>
@@ -547,6 +548,40 @@ spring.cloud.vault:
See also: https://www.vaultproject.io/docs/secrets/cassandra/index.html[Vault Documentation: Setting up Apache Cassandra with Vault]
[[vault-client-database-mongodb]]
=== MongoDB
Spring Cloud Vault can obtain credentials for MongoDB.
The integration can be enabled by setting
`spring.cloud.vault.mongodb.enabled=true` (default `false`) and
providing the role name with `spring.cloud.vault.mongodb.role=…`.
Username and password are stored in `spring.data.mongodb.username`
and `spring.data.mongodb.password` so using Spring Boot will
pick up the generated credentials without further configuration.
You can configure the property names by setting
`spring.cloud.vault.mongodb.username-property` and
`spring.cloud.vault.mongodb.password-property`.
[source,yaml]
----
spring.cloud.vault:
mongodb:
enabled: true
role: readonly
backend: mongodb
username-property: spring.data.mongodb.username
password-property: spring.data.mongodb.password
----
* `enabled` setting this value to `true` enables the MongodB backend config usage
* `role` sets the role name of the MongoDB role definition
* `backend` sets the path of the MongoDB mount to use
* `username-property` sets the property name in which the MongoDB username is stored
* `password-property` sets the property name in which the MongoDB password is stored
See also: https://www.vaultproject.io/docs/secrets/mongodb/index.html[Vault Documentation: Setting up MongoDB with Vault]
[[vault-client-database-mysql]]
=== MySQL

View File

@@ -33,6 +33,12 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
@@ -65,6 +71,13 @@
<version>2.1.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-core</artifactId>
<version>3.2.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -30,7 +30,9 @@ import org.springframework.util.Assert;
* @author Mark Paluch
*/
@Configuration
@EnableConfigurationProperties
@EnableConfigurationProperties({ VaultMySqlProperties.class,
VaultPostgreSqlProperties.class, VaultCassandraProperties.class,
VaultMongoProperties.class })
public class VaultConfigDatabaseBootstrapConfiguration {
@Bean
@@ -38,21 +40,6 @@ public class VaultConfigDatabaseBootstrapConfiguration {
return new DatabaseSecureBackendAccessorFactory();
}
@Bean
public VaultMySqlProperties mySqlProperties() {
return new VaultMySqlProperties();
}
@Bean
public VaultPostgreSqlProperties postgreSqlProperties() {
return new VaultPostgreSqlProperties();
}
@Bean
public VaultCassandraProperties cassandraProperties() {
return new VaultCassandraProperties();
}
static class DatabaseSecureBackendAccessorFactory
implements SecureBackendAccessorFactory<DatabaseSecretProperties> {
@@ -102,7 +89,7 @@ public class VaultConfigDatabaseBootstrapConfiguration {
public Map<String, String> transformProperties(
Map<String, String> input) {
Map<String, String> result = new HashMap();
Map<String, String> result = new HashMap<>();
result.put(properties.getUsernameProperty(), input.get("username"));
result.put(properties.getPasswordProperty(), input.get("password"));

View File

@@ -0,0 +1,59 @@
/*
* 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.databases;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
/**
* Configuration properties for Vault using the MongoDB integration.
*
* @author Mark Paluch
*/
@ConfigurationProperties("spring.cloud.vault.mongodb")
@Data
public class VaultMongoProperties implements DatabaseSecretProperties {
/**
* Enable mongodb backend usage.
*/
private boolean enabled = false;
/**
* Role name for credentials.
*/
private String role;
/**
* Cassandra backend path.
*/
@NotEmpty
private String backend = "mongodb";
/**
* Target property for the obtained username.
*/
@NotEmpty
private String usernameProperty = "spring.data.mongodb.username";
/**
* Target property for the obtained password.
*/
@NotEmpty
private String passwordProperty = "spring.data.mongodb.password";
}

View File

@@ -0,0 +1,97 @@
/*
* 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.databases;
import static org.assertj.core.api.Assertions.*;
import static org.junit.Assume.*;
import static org.springframework.cloud.vault.config.databases.VaultConfigDatabaseBootstrapConfiguration.DatabaseSecureBackendAccessorFactory.*;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cloud.vault.config.VaultConfigOperations;
import org.springframework.cloud.vault.config.VaultConfigTemplate;
import org.springframework.cloud.vault.config.VaultProperties;
import org.springframework.cloud.vault.util.CanConnect;
import org.springframework.cloud.vault.util.IntegrationTestSupport;
import org.springframework.cloud.vault.util.Settings;
import org.springframework.vault.core.VaultOperations;
/**
* Integration tests for {@link VaultConfigTemplate} using the mongodb secret backend.
* This test requires a running MongoDB instance, see {@link #ROOT_CREDENTIALS}.
*
* @author Mark Paluch
*/
public class MongoSecretIntegrationTests extends IntegrationTestSupport {
private final static int MONGODB_PORT = 27017;
private final static String MONGODB_HOST = "localhost";
private final static String ROOT_CREDENTIALS = String.format(
"mongodb://spring:vault@%s:%d/admin?ssl=false", MONGODB_HOST, MONGODB_PORT);
private final static String ROLES = "[ \"readWrite\", { \"role\": \"read\", \"db\": \"admin\" } ]";
private VaultProperties vaultProperties = Settings.createVaultProperties();
private VaultConfigOperations configOperations;
private VaultMongoProperties mongodb = new VaultMongoProperties();
/**
* Initialize the mongodb secret backend.
*
* @throws Exception
*/
@Before
public void setUp() throws Exception {
assumeTrue(CanConnect.to(new InetSocketAddress(MONGODB_HOST, MONGODB_PORT)));
mongodb.setEnabled(true);
mongodb.setRole("readonly");
if (!prepare().hasSecretBackend(mongodb.getBackend())) {
prepare().mountSecret(mongodb.getBackend());
}
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
vaultOperations.write(String.format("%s/config/connection", mongodb.getBackend()),
Collections.singletonMap("uri", ROOT_CREDENTIALS));
Map<String, String> role = new HashMap<>();
role.put("db", "admin");
role.put("roles", ROLES);
vaultOperations.write(
String.format("%s/roles/%s", mongodb.getBackend(), mongodb.getRole()),
role);
configOperations = new VaultConfigTemplate(vaultOperations, vaultProperties);
}
@Test
public void shouldCreateCredentialsCorrectly() throws Exception {
Map<String, String> secretProperties = configOperations
.read(forDatabase(mongodb));
assertThat(secretProperties).containsKeys("spring.data.mongodb.username",
"spring.data.mongodb.password");
}
}

View File

@@ -0,0 +1,129 @@
/*
* 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.databases;
import static org.junit.Assume.*;
import java.net.InetSocketAddress;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bson.Document;
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.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.vault.core.VaultOperations;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
/**
* Integration tests using the mongodb 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 = VaultConfigMongoTests.TestApplication.class, properties = {
"spring.cloud.vault.mongodb.enabled=true",
"spring.cloud.vault.mongodb.role=readonly",
"spring.data.mongodb.url=mongodb://localhost",
"spring.data.mongodb.database=admin" })
public class VaultConfigMongoTests {
private final static int MONGODB_PORT = 27017;
private final static String MONGODB_HOST = "localhost";
private final static String ROOT_CREDENTIALS = String.format(
"mongodb://spring:vault@%s:%d/admin?ssl=false", MONGODB_HOST, MONGODB_PORT);
private final static String ROLES = "[ \"readWrite\", { \"role\": \"read\", \"db\": \"admin\" } ]";
/**
* Initialize the mysql secret backend.
*
* @throws Exception
*/
@BeforeClass
public static void beforeClass() throws Exception {
assumeTrue(CanConnect.to(new InetSocketAddress(MONGODB_HOST, MONGODB_PORT)));
VaultRule vaultRule = new VaultRule();
vaultRule.before();
if (!vaultRule.prepare().hasSecretBackend("mongodb")) {
vaultRule.prepare().mountSecret("mongodb");
}
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
vaultOperations.write("mongodb/config/connection",
Collections.singletonMap("uri", ROOT_CREDENTIALS));
Map<String, String> role = new HashMap<>();
role.put("db", "admin");
role.put("roles", ROLES);
vaultOperations.write("mongodb/roles/readonly", role);
}
@Value("${spring.data.mongodb.username}")
String username;
@Value("${spring.data.mongodb.password}")
String password;
@Autowired
MongoClient mongoClient;
@Test
public void shouldConnectUsingDataSource() throws SQLException {
MongoDatabase mongoDatabase = mongoClient.getDatabase("admin");
List<Document> collections = mongoDatabase.listCollections()
.into(new ArrayList<Document>());
for (Document collection : collections) {
if (collection.getString("name").equals("hello")) {
mongoDatabase.getCollection(collection.getString("name")).drop();
}
}
mongoDatabase.createCollection("hello");
}
@SpringBootApplication
public static class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
}

View File

@@ -6,7 +6,7 @@
###########################################################################
VAULT_VER="0.6.0"
VAULT_VER="${VAULT_VER:-0.6.1}"
UNAME=$(uname -s | tr '[:upper:]' '[:lower:]')
VAULT_ZIP="vault_${VAULT_VER}_${UNAME}_amd64.zip"
IGNORE_CERTS="${IGNORE_CERTS:-no}"