Support MongoDB credential generation.
Fixes gh-28.
This commit is contained in:
11
.travis.yml
11
.travis.yml
@@ -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"
|
||||
|
||||
|
||||
@@ -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*
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"));
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}"
|
||||
|
||||
Reference in New Issue
Block a user