Support AWS credential generation

Tests require external provided access and secret keys to run.

fixes gh-4
This commit is contained in:
Mark Paluch
2016-07-03 13:56:08 +02:00
parent bd7b10aab0
commit c99a689ccc
9 changed files with 440 additions and 0 deletions

View File

@@ -26,6 +26,7 @@
<module>spring-cloud-vault-config-databases</module>
<module>spring-cloud-vault-config-consul</module>
<module>spring-cloud-vault-config-rabbitmq</module>
<module>spring-cloud-vault-config-aws</module>
<module>spring-cloud-vault-starter-config</module>
<module>docs</module>
</modules>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-parent</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-config-aws</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Vault Config AWS support</name>
<description>Spring Cloud Vault Config AWS support</description>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-aws</artifactId>
<version>1.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-core</artifactId>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-config</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,60 @@
/*
* 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.aws;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.vault.VaultSecretBackend;
import lombok.Data;
/**
* Configuration properties for Vault using the AWS integration.
*
* @author Mark Paluch
*/
@ConfigurationProperties("spring.cloud.vault.aws")
@Data
public class VaultAwsProperties implements VaultSecretBackend {
/**
* Enable aws backend usage.
*/
private boolean enabled = false;
/**
* Role name for credentials.
*/
private String role;
/**
* aws backend path.
*/
@NotEmpty
private String backend = "aws";
/**
* Target property for the obtained access key.
*/
@NotEmpty
private String accessKeyProperty = "cloud.aws.credentials.accessKey";
/**
* Target property for the obtained secret key.
*/
@NotEmpty
private String secretKeyProperty = "cloud.aws.credentials.secretKey";
}

View File

@@ -0,0 +1,104 @@
/*
* 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.aws;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.vault.SecureBackendAccessor;
import org.springframework.cloud.vault.VaultSecretBackend;
import org.springframework.cloud.vault.config.SecureBackendAccessorFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
/**
* @author Mark Paluch
*/
@Configuration
@EnableConfigurationProperties
public class VaultConfigAwsBootstrapConfiguration {
@Bean
public SecureBackendAccessorFactory<VaultAwsProperties> secureBackendAccessorFactory() {
return new AwsSecureBackendAccessorFactory();
}
@Bean
public VaultAwsProperties awsProperties() {
return new VaultAwsProperties();
}
static class AwsSecureBackendAccessorFactory
implements SecureBackendAccessorFactory<VaultAwsProperties> {
@Override
public SecureBackendAccessor createSecureBackendAccessor(
VaultAwsProperties properties) {
return forAws(properties);
}
@Override
public boolean supports(VaultSecretBackend secretBackend) {
return secretBackend instanceof VaultAwsProperties;
}
/**
* Creates a {@link SecureBackendAccessor} for a secure backend using
* {@link VaultAwsProperties}. This accessor transforms Vault's
* username/password property names to names provided with
* {@link VaultAwsProperties#getAccessKeyProperty()} and
* {@link VaultAwsProperties#getSecretKeyProperty()}.
*
* @param properties must not be {@literal null}.
* @return the {@link SecureBackendAccessor}
*/
public static SecureBackendAccessor forAws(
final VaultAwsProperties properties) {
Assert.notNull(properties, "VaultAwsProperties 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 String getName() {
return String.format("%s with Role %s", properties.getBackend(),
properties.getRole());
}
@Override
public Map<String, String> transformProperties(
Map<String, String> input) {
Map<String, String> result = new HashMap();
result.put(properties.getAccessKeyProperty(), input.get("access_key"));
result.put(properties.getSecretKeyProperty(), input.get("secret_key"));
return result;
}
};
}
}
}

View File

@@ -0,0 +1,5 @@
/**
* AWS integration with Vault.
* @author Mark Paluch
*/
package org.springframework.cloud.vault.config.aws;

View File

@@ -0,0 +1,3 @@
# Bootstrap Configuration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.vault.config.aws.VaultConfigAwsBootstrapConfiguration

View File

@@ -0,0 +1,95 @@
/*
* 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.aws;
import static org.assertj.core.api.Assertions.*;
import static org.junit.Assume.*;
import static org.springframework.cloud.vault.config.aws.VaultConfigAwsBootstrapConfiguration.AwsSecureBackendAccessorFactory.*;
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.AbstractIntegrationTests;
import org.springframework.cloud.vault.TestRestTemplateFactory;
import org.springframework.cloud.vault.VaultClient;
import org.springframework.cloud.vault.VaultProperties;
import org.springframework.cloud.vault.util.Settings;
import org.springframework.util.StringUtils;
/**
* Integration tests for {@link VaultClient} using the aws secret backend. This test
* requires AWS credentials and a region, see {@link #AWS_ACCESS_KEY} and
* {@link #AWS_SECRET_KEY} to be provided externally.
*
* @author Mark Paluch
*/
public class AwsSecretIntegrationTests extends AbstractIntegrationTests {
private final static String AWS_REGION = "eu-west-1";
private final static String AWS_ACCESS_KEY = System.getProperty("aws.access.key");
private final static String AWS_SECRET_KEY = System.getProperty("aws.secret.key");
private final static String ARN = "arn:aws:iam::aws:policy/ReadOnlyAccess";
private VaultProperties vaultProperties = Settings.createVaultProperties();
private VaultClient vaultClient = new VaultClient(vaultProperties);
private VaultAwsProperties aws = new VaultAwsProperties();
/**
* Initialize the aws secret backend.
*
* @throws Exception
*/
@Before
public void setUp() throws Exception {
assumeTrue(StringUtils.hasText(AWS_ACCESS_KEY)
&& StringUtils.hasText(AWS_SECRET_KEY));
aws.setEnabled(true);
aws.setRole("readonly");
if (!prepare().hasSecret(aws.getBackend())) {
prepare().mountSecret(aws.getBackend());
}
Map<String, String> connection = new HashMap<>();
connection.put("region", AWS_REGION);
connection.put("access_key", AWS_ACCESS_KEY);
connection.put("secret_key", AWS_SECRET_KEY);
prepare().write(String.format("%s/config/root", aws.getBackend()), connection);
prepare().write(String.format("%s/roles/%s", aws.getBackend(), aws.getRole()),
Collections.singletonMap("arn", ARN));
vaultClient.setRest(TestRestTemplateFactory.create(vaultProperties));
}
@Test
public void shouldCreateCredentialsCorrectly() throws Exception {
Map<String, String> secretProperties = vaultClient.read(forAws(aws),
Settings.token());
assertThat(secretProperties).containsKeys("cloud.aws.credentials.accessKey",
"cloud.aws.credentials.secretKey");
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.aws;
import static org.assertj.core.api.Assertions.*;
import static org.junit.Assume.*;
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.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.VaultRule;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;
/**
* Integration tests using the aws 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}.
* <p>
* This test requires AWS credentials and a region, see {@link #AWS_ACCESS_KEY},
* {@link #AWS_SECRET_KEY} and the {@link IntegrationTest} properties to be provided
* externally.
*
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = VaultConfigAwsTests.TestApplication.class)
@IntegrationTest({ "spring.cloud.vault.aws.enabled=true",
"spring.cloud.vault.aws.role=readonly", "cloud.aws.region.auto=false",
"cloud.aws.region.static=eu-west-1" })
public class VaultConfigAwsTests {
private final static String AWS_REGION = "eu-west-1";
private final static String AWS_ACCESS_KEY = System.getProperty("aws.access.key");
private final static String AWS_SECRET_KEY = System.getProperty("aws.secret.key");
private final static String ARN = "arn:aws:iam::aws:policy/ReadOnlyAccess";
/**
* Initialize the aws secret backend.
*
* @throws Exception
*/
@BeforeClass
public static void beforeClass() throws Exception {
assumeTrue(StringUtils.hasText(AWS_ACCESS_KEY)
&& StringUtils.hasText(AWS_SECRET_KEY));
VaultRule vaultRule = new VaultRule();
vaultRule.before();
if (!vaultRule.prepare().hasSecret("aws")) {
vaultRule.prepare().mountSecret("aws");
}
Map<String, String> connection = new HashMap<>();
connection.put("region", AWS_REGION);
connection.put("access_key", AWS_ACCESS_KEY);
connection.put("secret_key", AWS_SECRET_KEY);
vaultRule.prepare().write("aws/config/root", connection);
vaultRule.prepare().write("aws/roles/readonly",
Collections.singletonMap("arn", ARN));
}
@Value("${cloud.aws.credentials.accessKey}")
String accessKey;
@Value("${cloud.aws.credentials.secretKey}")
String secretKey;
@Test
public void shouldInitializeAwsProperties() throws Exception {
assertThat(accessKey).isNotEmpty();
assertThat(secretKey).isNotEmpty();
}
@SpringBootApplication
public static class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
}

View File

@@ -0,0 +1,5 @@
spring:
application.name: testVaultApp
cloud.vault.token: 00000000-0000-0000-0000-000000000000
cloud.vault.ssl.trust-store: file:../work/keystore.jks
cloud.vault.ssl.trust-store-password: changeit