diff --git a/.gitignore b/.gitignore
index 3c8daef1..63151953 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,5 +17,6 @@ _site/
download/
/vault/
/consul/
+/rabbitmq/
work
build/
diff --git a/.travis.yml b/.travis.yml
index 2960304d..13d8e01d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ language: java
services:
- mysql
- postgresql
+ - rabbitmq
jdk:
- oraclejdk8
@@ -18,6 +19,8 @@ install:
- src/test/bash/install_consul.sh
- src/test/bash/local_run_vault.sh &
- src/test/bash/local_run_consul.sh &
+ - sudo rabbitmq-plugins enable rabbitmq_management
+ - sudo service rabbitmq-server restart
before_script:
- mysql -e "CREATE USER 'spring' IDENTIFIED by 'vault';"
diff --git a/docs/src/main/asciidoc/spring-cloud-vault-config.adoc b/docs/src/main/asciidoc/spring-cloud-vault-config.adoc
index 3e793cbb..d3b3d3cb 100644
--- a/docs/src/main/asciidoc/spring-cloud-vault-config.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-vault-config.adoc
@@ -233,6 +233,28 @@ spring.cloud.vault:
See also: https://www.vaultproject.io/docs/secrets/consul/index.html[Vault Documentation: Setting up Consul with Vault]
+[[vault-client-rabbitmq]]
+=== RabbitMQ
+
+Spring Cloud Vault allows to obtain credentials for RabbitMQ.
+The integration can be enabled by setting `spring.cloud.vault.rabbit.enabled=true`
+(default `false`). Username and password are stored in `spring.rabbit.username`
+and `spring.rabbit.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.rabbit.username-property` and
+`spring.cloud.vault.rabbit.password-property`.
+
+[source,yaml]
+----
+spring.cloud.vault:
+ enabled: true
+ ...
+ rabbit:
+ enabled: true
+----
+
+See also: https://www.vaultproject.io/docs/secrets/rabbit/index.html[Vault Documentation: Setting up RabbitMQ with Vault]
+
[[vault-client-fail-fast]]
== Vault Client Fail Fast
diff --git a/spring-cloud-vault-config/pom.xml b/spring-cloud-vault-config/pom.xml
index 7291541b..7ecaf34e 100644
--- a/spring-cloud-vault-config/pom.xml
+++ b/spring-cloud-vault-config/pom.xml
@@ -57,6 +57,11 @@
spring-boot-starter-jdbc
test
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+ test
+
org.apache.httpcomponents
diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/VaultProperties.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/VaultProperties.java
index 0553be82..f26bf1e2 100644
--- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/VaultProperties.java
+++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/VaultProperties.java
@@ -102,6 +102,8 @@ public class VaultProperties {
private Consul consul = new Consul();
+ private Rabbitmq rabbitmq = new Rabbitmq();
+
/**
* Application name for AppId authentication.
*/
@@ -279,6 +281,38 @@ public class VaultProperties {
private String tokenProperty = "spring.cloud.consul.token";
}
+ @Data
+ public static class Rabbitmq implements DatabaseSecretProperties {
+
+ /**
+ * Enable rabbitmq backend usage.
+ */
+ private boolean enabled = false;
+
+ /**
+ * Role name for credentials.
+ */
+ private String role;
+
+ /**
+ * RabbitMQ backend path.
+ */
+ @NotEmpty
+ private String backend = "rabbitmq";
+
+ /**
+ * Target property for the obtained username.
+ */
+ @NotEmpty
+ private String usernameProperty = "spring.rabbitmq.username";
+
+ /**
+ * Target property for the obtained password.
+ */
+ @NotEmpty
+ private String passwordProperty = "spring.rabbitmq.password";
+ }
+
/**
* Configuration properties interface for database secrets.
*/
diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/VaultPropertySource.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/VaultPropertySource.java
index 6d727f56..a22761d8 100644
--- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/VaultPropertySource.java
+++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/VaultPropertySource.java
@@ -106,6 +106,11 @@ public class VaultPropertySource extends EnumerablePropertySource {
if (consul.isEnabled()) {
accessors.add(SecureBackendAccessors.consul(consul));
}
+
+ VaultProperties.Rabbitmq rabbitmq = vaultProperties.getRabbitmq();
+ if (rabbitmq.isEnabled()) {
+ accessors.add(SecureBackendAccessors.database(rabbitmq));
+ }
return accessors;
}
diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/RabbitMQSecretIntegrationTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/RabbitMQSecretIntegrationTests.java
new file mode 100644
index 00000000..b66f2aa2
--- /dev/null
+++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/RabbitMQSecretIntegrationTests.java
@@ -0,0 +1,96 @@
+/*
+ * 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.junit.Before;
+import org.junit.Test;
+import org.springframework.cloud.vault.util.CanConnect;
+import org.springframework.cloud.vault.util.Settings;
+
+/**
+ * Integration tests for {@link VaultClient} using the rabbitmq secret backend. This test
+ * requires a running RabbitMQ instance, see {@link #RABBITMQ_URI}.
+ *
+ * @author Mark Paluch
+ */
+public class RabbitMQSecretIntegrationTests extends AbstractIntegrationTests {
+
+ private final static int RABBITMQ_HTTP_MANAGEMENT_PORT = 15672;
+ private final static String RABBITMQ_HOST = "localhost";
+
+ private final static String RABBITMQ_USERNAME = "guest";
+ private final static String RABBITMQ_PASSWORD = "guest";
+
+ private final static String RABBITMQ_URI = String
+ .format("http://%s:%d", RABBITMQ_HOST, RABBITMQ_HTTP_MANAGEMENT_PORT);
+
+ private final static String VHOSTS_ROLE = "{\"/\":{\"write\": \".*\", \"read\": \".*\"}}";
+
+ private VaultProperties vaultProperties = Settings.createVaultProperties();
+ private VaultClient vaultClient = new VaultClient(vaultProperties);
+ private VaultProperties.Rabbitmq rabbitmq = vaultProperties.getRabbitmq();
+
+ /**
+ * Initialize the mysql secret backend.
+ *
+ * @throws Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+
+ assumeTrue(CanConnect.to(new InetSocketAddress(RABBITMQ_HOST, RABBITMQ_HTTP_MANAGEMENT_PORT)));
+
+ rabbitmq.setEnabled(true);
+ rabbitmq.setRole("readonly");
+
+ if (!prepare().hasSecret(rabbitmq.getBackend())) {
+ prepare().mountSecret(rabbitmq.getBackend());
+ }
+
+ Map connection = new HashMap<>();
+ connection.put("connection_uri", RABBITMQ_URI);
+ connection.put("username", RABBITMQ_USERNAME);
+ connection.put("password", RABBITMQ_PASSWORD);
+
+ prepare().write(String.format("%s/config/connection", rabbitmq.getBackend()),
+ connection);
+
+ prepare().write(String.format("%s/roles/%s", rabbitmq.getBackend(), rabbitmq.getRole()),
+ Collections.singletonMap("vhosts", VHOSTS_ROLE));
+
+ vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
+ }
+
+ @Test
+ public void shouldCreateCredentialsCorrectly() throws Exception {
+
+ Map secretProperties = vaultClient.read(database(rabbitmq),
+ Settings.token());
+
+ assertThat(secretProperties).containsKeys("spring.rabbitmq.username",
+ "spring.rabbitmq.password");
+ }
+
+}
diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/configclient/VaultRabbitMQTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/configclient/VaultRabbitMQTests.java
new file mode 100644
index 00000000..357ce7c7
--- /dev/null
+++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/configclient/VaultRabbitMQTests.java
@@ -0,0 +1,136 @@
+/*
+ * 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.configclient;
+
+import static org.junit.Assume.*;
+
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+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.IntegrationTest;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.cloud.vault.util.CanConnect;
+import org.springframework.cloud.vault.util.VaultRule;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.rabbitmq.client.Channel;
+import com.rabbitmq.client.Connection;
+import com.rabbitmq.client.ConnectionFactory;
+
+/**
+ * Integration tests using the rabbitmq 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)
+@SpringApplicationConfiguration(classes = VaultRabbitMQTests.TestApplication.class)
+@IntegrationTest({ "spring.cloud.vault.rabbitmq.enabled=true",
+ "spring.cloud.vault.rabbitmq.role=readonly",
+ "spring.rabbitmq.address=localhost" })
+public class VaultRabbitMQTests {
+
+ private final static int RABBITMQ_HTTP_MANAGEMENT_PORT = 15672;
+ private final static int RABBITMQ_PORT = 5672;
+ private final static String RABBITMQ_HOST = "localhost";
+
+ private final static String RABBITMQ_USERNAME = "guest";
+ private final static String RABBITMQ_PASSWORD = "guest";
+
+ private final static String RABBITMQ_URI = String.format("http://%s:%d",
+ RABBITMQ_HOST, RABBITMQ_HTTP_MANAGEMENT_PORT);
+
+ private final static String VHOSTS_ROLE = "{\"/\":{\"write\": \".*\", \"read\": \".*\"}}";
+
+ /**
+ * Initialize the rabbitmq secret backend.
+ *
+ * @throws Exception
+ */
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+
+ assumeTrue(CanConnect
+ .to(new InetSocketAddress(RABBITMQ_HOST, RABBITMQ_HTTP_MANAGEMENT_PORT)));
+
+ VaultRule vaultRule = new VaultRule();
+ vaultRule.before();
+
+ if (!vaultRule.prepare().hasSecret("rabbitmq")) {
+ vaultRule.prepare().mountSecret("rabbitmq");
+ }
+
+ Map connection = new HashMap<>();
+ connection.put("connection_uri", RABBITMQ_URI);
+ connection.put("username", RABBITMQ_USERNAME);
+ connection.put("password", RABBITMQ_PASSWORD);
+
+ vaultRule.prepare().write(String.format("rabbitmq/config/connection"),
+ connection);
+
+ vaultRule.prepare().write(String.format("rabbitmq/roles/readonly"),
+ Collections.singletonMap("vhosts", VHOSTS_ROLE));
+ }
+
+ @Value("${spring.rabbitmq.username}")
+ String username;
+
+ @Value("${spring.rabbitmq.password}")
+ String password;
+
+ @Autowired
+ org.springframework.amqp.rabbit.connection.ConnectionFactory connectionFactory;
+
+ @Test
+ public void shouldConnectSpringConnectionFactory() {
+ connectionFactory.createConnection().close();
+ }
+
+ @Test
+ public void shouldConnectUsingRabbitMQClient() throws Exception {
+
+ ConnectionFactory factory = new ConnectionFactory();
+ factory.setHost(RABBITMQ_HOST);
+ factory.setPort(RABBITMQ_PORT);
+ factory.setUsername(username);
+ factory.setPassword(password);
+ Connection connection = factory.newConnection();
+ Channel channel = connection.createChannel();
+
+ channel.close();
+ connection.close();
+ }
+
+ @SpringBootApplication
+ public static class TestApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TestApplication.class, args);
+ }
+ }
+}
diff --git a/src/test/bash/install_rabbitmq.sh b/src/test/bash/install_rabbitmq.sh
new file mode 100755
index 00000000..60877747
--- /dev/null
+++ b/src/test/bash/install_rabbitmq.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+###########################################################################
+# Download and Install RabbitMQ #
+# This script is prepared for caching of the download directory #
+###########################################################################
+
+
+RABBITMQ_VER="3.6.2"
+UNAME=$(uname -s | tr '[:upper:]' '[:lower:]')
+
+if [[ ${UNAME} == "darwin" ]] ; then
+ RABBITMQ_ZIP="rabbitmq-server-mac-standalone-${RABBITMQ_VER}.tar.xz"
+else
+ echo "Installation of RabbitMQ on ${UNAME} not supported by this script"
+ exit 1
+fi
+IGNORE_CERTS="${IGNORE_CERTS:-no}"
+
+# cleanup
+mkdir -p rabbitmq
+
+if [[ ! -f "download/${RABBITMQ_ZIP}" ]] ; then
+ cd download
+ # install Vault
+ if [[ "${IGNORE_CERTS}" == "no" ]] ; then
+ echo "Downloading RabbitMQ with certs verification"
+ wget "https://www.rabbitmq.com/releases/rabbitmq-server/v${RABBITMQ_VER}/${RABBITMQ_ZIP}"
+ else
+ echo "WARNING... Downloading RabbitMQ WITHOUT certs verification"
+ wget "https://www.rabbitmq.com/releases/rabbitmq-server/v${RABBITMQ_VER}/${RABBITMQ_ZIP}" --no-check-certificate
+ fi
+
+ if [[ $? != 0 ]] ; then
+ echo "Cannot download RabbitMQ"
+ exit 1
+ fi
+ cd ..
+fi
+
+cd rabbitmq
+
+if [[ -d ebin ]] ; then
+ rm -Rf ../rabbitmq/*
+fi
+
+tar xzf ../download/${RABBITMQ_ZIP}
diff --git a/src/test/bash/local_run_consul.sh b/src/test/bash/local_run_consul.sh
index ee604fe3..0ae7660d 100755
--- a/src/test/bash/local_run_consul.sh
+++ b/src/test/bash/local_run_consul.sh
@@ -6,7 +6,6 @@
BASEDIR=`dirname $0`/../../..
-#!/bin/bash
mkdir -p ${BASEDIR}/consul/config
mkdir -p ${BASEDIR}/consul/data
diff --git a/src/test/bash/local_run_rabbitmq.sh b/src/test/bash/local_run_rabbitmq.sh
new file mode 100755
index 00000000..9146e988
--- /dev/null
+++ b/src/test/bash/local_run_rabbitmq.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+###########################################################################
+# Start RabbitMQ on localhost #
+###########################################################################
+
+BASEDIR=`dirname $0`/../../..
+
+RABBITMQ_VER="3.6.2"
+
+cd rabbitmq/rabbitmq_server-${RABBITMQ_VER}
+
+if [[ ! -f etc/rabbitmq/rabbitmq.config ]] ; then
+ cp etc/rabbitmq/rabbitmq.config.example etc/rabbitmq/rabbitmq.config
+fi
+
+sbin/rabbitmq-plugins enable rabbitmq_management
+sbin/rabbitmq-server
+
+exit $?
diff --git a/src/test/bash/local_run_vault.sh b/src/test/bash/local_run_vault.sh
index 297ab467..fc00213c 100755
--- a/src/test/bash/local_run_vault.sh
+++ b/src/test/bash/local_run_vault.sh
@@ -5,5 +5,7 @@
###########################################################################
BASEDIR=`dirname $0`/../../..
+
./vault/vault server -config=${BASEDIR}/spring-cloud-vault-config/src/test/resources/vault.conf
+
exit $?