Add support for AWS STS credential types.

We now support assumed role and federation tokens.

Closes gh-572
Original pull request gh-575.
This commit is contained in:
krisiye
2021-02-09 17:46:41 -05:00
committed by Mark Paluch
parent 0c9a2b9b46
commit 2e40128024
4 changed files with 213 additions and 23 deletions

View File

@@ -196,10 +196,21 @@ dependency.
The integration can be enabled by setting
`spring.cloud.vault.aws=true` (default `false`) and providing the role name with `spring.cloud.vault.aws.role=…`.
Supported AWS credential Types:
* iam_user (Defaults)
* assumed_role (STS)
* federation_token (STS)
The access key and secret key are stored in `cloud.aws.credentials.accessKey`
and `cloud.aws.credentials.secretKey` so using Spring Cloud AWS will pick up the generated credentials without further configuration.
and `cloud.aws.credentials.secretKey`. So using Spring Cloud AWS will pick up the generated credentials without further configuration.
You can configure the property names by setting `spring.cloud.vault.aws.access-key-property` and
`spring.cloud.vault.aws.secret-key-property`.
`spring.cloud.vault.aws.secret-key-property`.
For STS security token, you can configure the property name by setting `spring.cloud.vault.aws.session-token-key-property`. The security token is stored under `cloud.aws.credentials.sessionToken` (defaults).
Example: iam_user
====
[source,yaml]
@@ -214,11 +225,33 @@ spring.cloud.vault:
----
====
Example: assumed_role (STS)
====
[source,yaml]
----
spring.cloud.vault:
aws:
enabled: true
role: sts-vault-role
backend: aws
credential-type: assumed_role
access-key-property: cloud.aws.credentials.accessKey
secret-key-property: cloud.aws.credentials.secretKey
session-token-key-property: cloud.aws.credentials.sessionToken
ttl: 3600s
role-arn: arn:aws:iam::${AWS_ACCOUNT}:role/sts-app-role
----
====
* `enabled` setting this value to `true` enables the AWS backend config usage
* `role` sets the role name of the AWS role definition
* `backend` sets the path of the AWS mount to use
* `access-key-property` sets the property name in which the AWS access key is stored
* `secret-key-property` sets the property name in which the AWS secret key is stored
* `session-token-key-property` sets the property name in which the AWS STS security token is stored.
* `credential-type` sets the aws credential type to use for this backend. Defaults to `iam_user`
* `ttl` sets the ttl for the STS token when using `assumed_role` or `federation_token`. Defaults to the ttl specified by the vault role. Min/Max values are also limited to what AWS would support for STS.
* `role-arn` sets the IAM role to assume if more than one are configured for the vault role when using `assumed_role`.
See also: https://www.vaultproject.io/docs/secrets/aws/index.html[Vault Documentation: Setting up AWS with Vault]

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2013-2020 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
*
* https://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.
*/
/**
* @author Kris Iyer
*
* Supported Aws credential types.
*/
package org.springframework.cloud.vault.config.aws;
public enum AwsCredentialType {
IAM_USER, ASSUMED_ROLE, FEDERATION_TOKEN
}

View File

@@ -24,6 +24,7 @@ import org.springframework.lang.Nullable;
* Configuration properties for Vault using the AWS integration.
*
* @author Mark Paluch
* @author Kris Iyer
*/
@ConfigurationProperties("spring.cloud.vault.aws")
public class VaultAwsProperties implements VaultSecretBackendDescriptor {
@@ -44,6 +45,11 @@ public class VaultAwsProperties implements VaultSecretBackendDescriptor {
*/
private String backend = "aws";
/**
* aws credential type
*/
private AwsCredentialType credentialType = AwsCredentialType.IAM_USER;
/**
* Target property for the obtained access key.
*/
@@ -54,6 +60,24 @@ public class VaultAwsProperties implements VaultSecretBackendDescriptor {
*/
private String secretKeyProperty = "cloud.aws.credentials.secretKey";
/**
* Target property for the obtained secret key.
*/
private String sessionTokenKeyProperty = "cloud.aws.credentials.sessionToken";
/**
*
* Role arn for assumed_role in case we have multiple roles associated with the vault
* role
*/
private String roleArn;
/**
* TTL for sts tokens. Defaults to whatever the vault Role may have for Max. Also
* limited to what AWS supports to be the max for STS.
*/
private String ttl;
@Override
public boolean isEnabled() {
return this.enabled;
@@ -97,4 +121,36 @@ public class VaultAwsProperties implements VaultSecretBackendDescriptor {
this.secretKeyProperty = secretKeyProperty;
}
public AwsCredentialType getCredentialType() {
return credentialType;
}
public void setCredentialType(AwsCredentialType credentialType) {
this.credentialType = credentialType;
}
public String getSessionTokenKeyProperty() {
return sessionTokenKeyProperty;
}
public void setSessionTokenKeyProperty(String sessionTokenKeyProperty) {
this.sessionTokenKeyProperty = sessionTokenKeyProperty;
}
public String getRoleArn() {
return roleArn;
}
public void setRoleArn(String roleArn) {
this.roleArn = roleArn;
}
public String getTtl() {
return ttl;
}
public void setTtl(String ttl) {
this.ttl = ttl;
}
}

View File

@@ -21,6 +21,7 @@ import java.util.Map;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.vault.config.LeasingSecretBackendMetadata;
import org.springframework.cloud.vault.config.PropertyNameTransformer;
import org.springframework.cloud.vault.config.SecretBackendMetadata;
import org.springframework.cloud.vault.config.SecretBackendMetadataFactory;
@@ -28,12 +29,17 @@ import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.vault.core.lease.domain.RequestedSecret;
import org.springframework.vault.core.lease.domain.RequestedSecret.Mode;
import org.springframework.vault.core.util.PropertyTransformer;
/**
* Bootstrap configuration providing support for the AWS secret backend.
*
* @author Mark Paluch
* @author Kris Iyer
*
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(VaultAwsProperties.class)
@@ -57,6 +63,7 @@ public class VaultConfigAwsBootstrapConfiguration {
* property names to names provided with
* {@link VaultAwsProperties#getAccessKeyProperty()} and
* {@link VaultAwsProperties#getSecretKeyProperty()}.
* {@link VaultAwsProperties#getSessionTokenKeyProperty()}.
* @param properties must not be {@literal null}.
* @return the {@link SecretBackendMetadata}
*/
@@ -68,34 +75,100 @@ public class VaultConfigAwsBootstrapConfiguration {
transformer.addKeyTransformation("access_key", properties.getAccessKeyProperty());
transformer.addKeyTransformation("secret_key", properties.getSecretKeyProperty());
return new SecretBackendMetadata() {
if (properties.getCredentialType() == AwsCredentialType.ASSUMED_ROLE
|| properties.getCredentialType() == AwsCredentialType.FEDERATION_TOKEN) {
@Override
public String getName() {
return String.format("%s with Role %s", properties.getBackend(), properties.getRole());
}
// security token transformer for STS
transformer.addKeyTransformation("security_token", properties.getSessionTokenKeyProperty());
@Override
public String getPath() {
return String.format("%s/creds/%s", properties.getBackend(), properties.getRole());
}
return new LeasingSecretBackendMetadata() {
@Override
public PropertyTransformer getPropertyTransformer() {
return transformer;
}
@Override
public String getName() {
return String.format("%s with Role %s", properties.getBackend(), properties.getRole());
}
@Override
public Map<String, String> getVariables() {
@Override
public String getPath() {
String defaultPath = "%s/sts/%s";
Map<String, String> variables = new HashMap<>();
// do we have any ttl or role parameters configured?
if (StringUtils.hasText(properties.getTtl())
|| (properties.getCredentialType() == AwsCredentialType.ASSUMED_ROLE
&& StringUtils.hasText(properties.getRoleArn()))) {
defaultPath += "?";
}
// ttl for assumed_role or federation_token
// pass through to let aws take care of min and max validations
// per
// the vault role
if (StringUtils.hasText(properties.getTtl())) {
defaultPath += "ttl=" + properties.getTtl() + "&";
}
variables.put("backend", properties.getBackend());
variables.put("key", String.format("creds/%s", properties.getRole()));
// role_arn for assumed_role for vault role that has multiple role
// associations.
if (properties.getCredentialType() == AwsCredentialType.ASSUMED_ROLE
&& StringUtils.hasText(properties.getRoleArn())) {
defaultPath += "role_arn=" + properties.getRoleArn();
}
return String.format(defaultPath, properties.getBackend(), properties.getRole());
}
return variables;
}
};
@Override
public PropertyTransformer getPropertyTransformer() {
return transformer;
}
@Override
public Map<String, String> getVariables() {
Map<String, String> variables = new HashMap<>();
variables.put("backend", properties.getBackend());
variables.put("key", String.format("sts/%s", properties.getRole()));
return variables;
}
@Override
public Mode getLeaseMode() {
return RequestedSecret.Mode.ROTATE;
}
};
}
else {
return new SecretBackendMetadata() {
@Override
public String getName() {
return String.format("%s with Role %s", properties.getBackend(), properties.getRole());
}
@Override
public String getPath() {
return String.format("%s/creds/%s", properties.getBackend(), properties.getRole());
}
@Override
public PropertyTransformer getPropertyTransformer() {
return transformer;
}
@Override
public Map<String, String> getVariables() {
Map<String, String> variables = new HashMap<>();
variables.put("backend", properties.getBackend());
variables.put("key", String.format("creds/%s", properties.getRole()));
return variables;
}
};
}
}
@Override