Support Consul credential generation
Fixes gh-5
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -15,7 +15,7 @@ _site/
|
||||
*.iws
|
||||
.factorypath
|
||||
download/
|
||||
vault/download
|
||||
vault/vault
|
||||
/vault/
|
||||
/consul/
|
||||
work
|
||||
build/
|
||||
|
||||
@@ -15,7 +15,9 @@ install:
|
||||
- apache-cassandra-2.2.6/bin/cassandra
|
||||
- src/test/bash/create_certificates.sh
|
||||
- src/test/bash/install_vault.sh
|
||||
- src/test/bash/install_consul.sh
|
||||
- src/test/bash/local_run_vault.sh &
|
||||
- src/test/bash/local_run_consul.sh &
|
||||
|
||||
before_script:
|
||||
- mysql -e "CREATE USER 'spring' IDENTIFIED by 'vault';"
|
||||
|
||||
@@ -113,15 +113,15 @@ spring.cloud.vault:
|
||||
----
|
||||
|
||||
[source,yaml]
|
||||
MyUserIdMechanism.java
|
||||
.MyUserIdMechanism.java
|
||||
----
|
||||
public class MyUserIdMechanism implements AppIdUserIdMechanism {
|
||||
|
||||
@Override
|
||||
public String createUserId() {
|
||||
String userId = ...
|
||||
return userId;
|
||||
}
|
||||
@Override
|
||||
public String createUserId() {
|
||||
String userId = ...
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
@@ -151,7 +151,7 @@ JDBC secrets need to be configured separately.
|
||||
|
||||
Spring Cloud Vault allows to obtain credentials for Apache Cassandra.
|
||||
The integration can be enabled by setting `spring.cloud.vault.cassandra.enabled=true`
|
||||
(default "false"). Username and password are stored in `spring.data.cassandra.username`
|
||||
(default `false`). Username and password are stored in `spring.data.cassandra.username`
|
||||
and `spring.data.cassandra.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.cassandra.username-property` and
|
||||
@@ -173,7 +173,7 @@ See also: https://www.vaultproject.io/docs/secrets/cassandra/index.html[Vault Do
|
||||
|
||||
Spring Cloud Vault allows to obtain credentials for MySQL.
|
||||
The integration can be enabled by setting `spring.cloud.vault.mysql.enabled=true`
|
||||
(default "false"). Username and password are stored in `spring.datasource.username`
|
||||
(default `false`). 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. You can configure the property names
|
||||
by setting `spring.cloud.vault.mysql.username-property` and
|
||||
@@ -195,7 +195,7 @@ See also: https://www.vaultproject.io/docs/secrets/mysql/index.html[Vault Docume
|
||||
|
||||
Spring Cloud Vault allows to obtain credentials for PostgreSQL.
|
||||
The integration can be enabled by setting `spring.cloud.vault.postgresql.enabled=true`
|
||||
(default "false"). Username and password are stored in `spring.datasource.username`
|
||||
(default `false`). 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. You can configure the property names
|
||||
by setting `spring.cloud.vault.postgresql.username-property` and
|
||||
@@ -212,6 +212,28 @@ spring.cloud.vault:
|
||||
|
||||
See also: https://www.vaultproject.io/docs/secrets/postgresql/index.html[Vault Documentation: Setting up PostgreSQL with Vault]
|
||||
|
||||
[[vault-client-consul]]
|
||||
== Consul
|
||||
|
||||
Spring Cloud Vault allows to obtain credentials for Hashicorp Consil.
|
||||
The integration can be enabled by setting `spring.cloud.vault.consul.enabled=true`
|
||||
(default `false`). The obtained token is stored in `spring.cloud.consul.token`
|
||||
so using Spring Cloud Consul can pick up the generated
|
||||
credentials without further configuration. You can configure the property name
|
||||
by setting `spring.cloud.vault.consul.token-property`.
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
spring.cloud.vault:
|
||||
enabled: true
|
||||
...
|
||||
consul:
|
||||
enabled: true
|
||||
----
|
||||
|
||||
See also: https://www.vaultproject.io/docs/secrets/consul/index.html[Vault Documentation: Setting up Consul with Vault]
|
||||
|
||||
|
||||
[[vault-client-fail-fast]]
|
||||
== Vault Client Fail Fast
|
||||
|
||||
|
||||
@@ -110,4 +110,40 @@ public class SecureBackendAccessors {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link SecureBackendAccessor} for a secure backend using
|
||||
* {@link org.springframework.cloud.vault.VaultProperties.Consul}.
|
||||
* This accessor transforms Vault's token property names to names provided
|
||||
* with {@link VaultProperties.Consul#getTokenProperty()}.
|
||||
*
|
||||
* @param properties must not be {@literal null}.
|
||||
* @return the {@link SecureBackendAccessor}
|
||||
*/
|
||||
public static SecureBackendAccessor consul(
|
||||
final VaultProperties.Consul properties) {
|
||||
|
||||
Assert.notNull(properties, "Consul properties must not be null");
|
||||
|
||||
return new SecureBackendAccessor() {
|
||||
|
||||
@Override
|
||||
public Map<String, String> variables() {
|
||||
|
||||
Map<String, String> variables = new HashMap<>();
|
||||
variables.put("backend", properties.getBackend());
|
||||
variables.put("key", String.format("creds/%s", properties.getRole()));
|
||||
return variables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> transformProperties(Map<String, String> input) {
|
||||
|
||||
Map<String, String> result = new HashMap();
|
||||
result.put(properties.getTokenProperty(), input.get("token"));
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,8 @@ public class VaultProperties {
|
||||
|
||||
private Cassandra cassandra = new Cassandra();
|
||||
|
||||
private Consul consul = new Consul();
|
||||
|
||||
/**
|
||||
* Application name for AppId authentication.
|
||||
*/
|
||||
@@ -251,6 +253,32 @@ public class VaultProperties {
|
||||
private String passwordProperty = "spring.data.cassandra.password";
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Consul {
|
||||
|
||||
/**
|
||||
* Enable consul backend usage.
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
/**
|
||||
* Role name for credentials.
|
||||
*/
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* Consul backend path.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String backend = "consul";
|
||||
|
||||
/**
|
||||
* Target property for the obtained token.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String tokenProperty = "spring.cloud.consul.token";
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration properties interface for database secrets.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.springframework.cloud.vault.SecureBackendAccessors.*;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.cloud.vault.util.CanConnect;
|
||||
import org.springframework.cloud.vault.util.Settings;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultClient} using the consul secret backend. This test
|
||||
* requires a running Consul instance, see {@link #CONNECTION_URL}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class ConsulSecretIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
private final static String CONSUL_HOST = "localhost";
|
||||
private final static int CONSUL_PORT = 8500;
|
||||
|
||||
private final static String CONNECTION_URL = String.format("%s:%d", CONSUL_HOST,
|
||||
CONSUL_PORT);
|
||||
|
||||
private final static String POLICY = "key \"\" { policy = \"read\" }";
|
||||
private final static String CONSUL_ACL_MASTER_TOKEN = "consul-master-token";
|
||||
|
||||
private final static ParameterizedTypeReference<Map<String, String>> STRING_MAP = new ParameterizedTypeReference<Map<String, String>>() {
|
||||
};
|
||||
|
||||
private VaultProperties vaultProperties = Settings.createVaultProperties();
|
||||
private VaultClient vaultClient = new VaultClient(vaultProperties);
|
||||
private VaultProperties.Consul consul = vaultProperties.getConsul();
|
||||
|
||||
private TestRestTemplate restTemplate = new TestRestTemplate();
|
||||
|
||||
/**
|
||||
* Initialize the postgresql secret backend.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
assumeTrue(CanConnect.to(new InetSocketAddress(CONSUL_HOST, CONSUL_PORT)));
|
||||
|
||||
consul.setEnabled(true);
|
||||
consul.setRole("readonly");
|
||||
|
||||
if (!prepare().hasSecret(consul.getBackend())) {
|
||||
prepare().mountSecret(consul.getBackend());
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("X-Consul-Token", CONSUL_ACL_MASTER_TOKEN);
|
||||
HttpEntity<String> requestEntity = new HttpEntity<>(
|
||||
"{\"Name\": \"sample\", \"Type\": \"management\"}", headers);
|
||||
ResponseEntity<Map<String, String>> tokenResponse = restTemplate.exchange(
|
||||
"http://{address}/v1/acl/create", HttpMethod.PUT, requestEntity,
|
||||
STRING_MAP, CONNECTION_URL);
|
||||
|
||||
Map<String, String> consulAccess = new HashMap<>();
|
||||
consulAccess.put("address", CONNECTION_URL);
|
||||
consulAccess.put("token", tokenResponse.getBody().get("ID"));
|
||||
|
||||
prepare().write(String.format("%s/config/access", consul.getBackend()),
|
||||
consulAccess);
|
||||
|
||||
prepare().write(
|
||||
String.format("%s/roles/%s", consul.getBackend(), consul.getRole()),
|
||||
Collections.singletonMap("policy",
|
||||
Base64.encodeBase64String(POLICY.getBytes())));
|
||||
|
||||
vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCredentialsCorrectly() throws Exception {
|
||||
|
||||
Map<String, String> secretProperties = vaultClient.read(consul(consul),
|
||||
Settings.token());
|
||||
|
||||
assertThat(secretProperties).containsKeys("spring.cloud.consul.token");
|
||||
}
|
||||
}
|
||||
8
spring-cloud-vault-config/src/test/resources/consul.json
Normal file
8
spring-cloud-vault-config/src/test/resources/consul.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"log_level": "INFO",
|
||||
"server": true,
|
||||
"datacenter": "dc1",
|
||||
"advertise_addr": "127.0.0.1",
|
||||
"acl_datacenter": "dc1",
|
||||
"acl_master_token": "consul-master-token"
|
||||
}
|
||||
45
src/test/bash/install_consul.sh
Executable file
45
src/test/bash/install_consul.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
###########################################################################
|
||||
# Download and Install Consul #
|
||||
# This script is prepared for caching of the download directory #
|
||||
###########################################################################
|
||||
|
||||
|
||||
CONSUL_VER="0.6.3"
|
||||
UNAME=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
CONSUL_ZIP="consul_${CONSUL_VER}_${UNAME}_amd64.zip"
|
||||
IGNORE_CERTS="${IGNORE_CERTS:-no}"
|
||||
|
||||
# cleanup
|
||||
mkdir -p consul
|
||||
|
||||
if [[ ! -f "download/${CONSUL_ZIP}" ]] ; then
|
||||
cd download
|
||||
# install Vault
|
||||
if [[ "${IGNORE_CERTS}" == "no" ]] ; then
|
||||
echo "Downloading Consul with certs verification"
|
||||
wget "https://releases.hashicorp.com/consul/${CONSUL_VER}/${CONSUL_ZIP}"
|
||||
else
|
||||
echo "WARNING... Downloading Consul WITHOUT certs verification"
|
||||
wget "https://releases.hashicorp.com/consul/${CONSUL_VER}/${CONSUL_ZIP}" --no-check-certificate
|
||||
fi
|
||||
|
||||
if [[ $? != 0 ]] ; then
|
||||
echo "Cannot download Consul"
|
||||
exit 1
|
||||
fi
|
||||
cd ..
|
||||
fi
|
||||
|
||||
cd consul
|
||||
|
||||
if [[ -f consul ]] ; then
|
||||
rm consul
|
||||
fi
|
||||
|
||||
unzip ../download/${CONSUL_ZIP}
|
||||
chmod a+x consul
|
||||
|
||||
# check
|
||||
./consul --version
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
###########################################################################
|
||||
# Download and Install Vault #
|
||||
# This script is prepared for caching of the vault/download directory #
|
||||
# This script is prepared for caching of the download directory #
|
||||
###########################################################################
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ if [[ ! -f "download/${VAULT_ZIP}" ]] ; then
|
||||
fi
|
||||
|
||||
if [[ $? != 0 ]] ; then
|
||||
echo "Cannot download vault"
|
||||
echo "Cannot download Vault"
|
||||
exit 1
|
||||
fi
|
||||
cd ..
|
||||
|
||||
18
src/test/bash/local_run_consul.sh
Executable file
18
src/test/bash/local_run_consul.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
###########################################################################
|
||||
# Start Consul on localhost:8500 #
|
||||
###########################################################################
|
||||
|
||||
BASEDIR=`dirname $0`/../../..
|
||||
|
||||
#!/bin/bash
|
||||
mkdir -p ${BASEDIR}/consul/config
|
||||
mkdir -p ${BASEDIR}/consul/data
|
||||
|
||||
./consul/consul agent -server \
|
||||
-bootstrap-expect 1 \
|
||||
-data-dir ${BASEDIR}/consul/data \
|
||||
-config-file=${BASEDIR}/spring-cloud-vault-config/src/test/resources/consul.json
|
||||
|
||||
exit $?
|
||||
Reference in New Issue
Block a user