AWS STS with Spring Cloud Vault
In my last post “Spring Boot Configuration and Secret Management Patterns on Kubernetes” I touched on some integration patterns for secret management with Spring Cloud Vault. Along with that I also highlighted that one of the issues I was working on was about enabling AWS STS for Spring Cloud Vault. This is now available with spring cloud 2020.0.2
!!!
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Notice the new Release Train versioning naming convention!
AWS Security Token Service (AWS STS)
AWS Security Token Service (AWS STS) is a web service that enables you to request temporary, limited-privilege credentials for AWS Identity and Access Management (IAM) users or for users that you authenticate (federated users). The key purpose of AWS STS is to allow a user or an application to assume a role and obtain access to AWS services or resources. For more information, see Temporary Security Credentials in the IAM User Guide.
For applications it's no different, and we could have the application assume the role and request temporary credentials to AWS resources such as EC2, S3, etc. This is where Spring Cloud Vault combined with AWS Secrets backend on Vault provides the capability for a Spring Boot application to use dynamic credentials.
Vault AWS Secret Backend
The AWS secrets engine generates AWS access credentials dynamically based on IAM policies. The AWS IAM credentials are time-based and are automatically revoked when the Vault lease expires.
Vault supports three different types of credentials to retrieve from AWS:
iam_user
: Vault will create an IAM user for each lease, attach the managed and inline IAM policies as specified in the role to the user, and if a permissions boundary is specified on the role, the permissions boundary will also be attached. Vault will then generate an access key and secret key for the IAM user and return them to the caller. IAM users have no session tokens and so no session token will be returned. Vault will delete the IAM user upon reaching the TTL expiration.assumed_role
: Vault will call sts:AssumeRole and return the access key, secret key, and session token to the caller.federation_token
: Vault will call sts:GetFederationToken passing in the supplied AWS policy document and return the access key, secret key, and session token to the caller.
More details on the setup can be found under AWS Secrets Engine.
Spring Cloud Vault
The AWS secret engine can be enabled by adding the spring-cloud-vault-config-aws
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-config-aws</artifactId>
<version>3.0.2</version>
</dependency>
</dependencies>
The AWS secret integration now supports the notion of a credential-type
which defaults to iam_user
for backward compatibility.
Sample iam_user configuration:
spring.cloud.vault:
aws:
enabled: true
role: readonly
backend: aws
access-key-property: cloud.aws.credentials.accessKey
secret-key-property: cloud.aws.credentials.secretKey
enabled
setting this value totrue
enables the AWS backend config usagerole
sets the role name of the AWS role definitionbackend
sets the path of the AWS mount to useaccess-key-property
sets the property name in which the AWS access key is storedsecret-key-property
sets the property name in which the AWS secret key is stored
For AWS STS supported values forcredential-type
are assumed_role
or federation_token
.
Sample assume_role configuration:
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
New additions STS configuration:
session-token-key-property
sets the property name in which the AWS STS security token is storedcredential-type
sets the AWS credential type to use for this backend. Defaults toiam_user
ttl
sets the TTL for the STS token when usingassumed_role
orfederation_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 is configured for the vault role when usingassumed_role
Please read Spring Cloud Vault AWS Backend for more details on this integration.
Lease Rotation and Property Sources
STS credentials default to a TTL of 60 mins. You can adjust the TTL based on your requirement. It's important to note that min/max TTL values allowed are as per what AWS STS would allow for configuration.
For assumed_role
we could set that between a minimum of 900 seconds (15 minutes) up to the maximum session duration setting for the role which could be anywhere between 3,600s (1 hour) and 43,200s (12 hours). The default expiration period for federation_token
is substantially longer (12 hours instead of one hour compared to assumed_role
) and we could specify the duration between 900 seconds (15 minutes) to 129,600 seconds (36 hours).
Spring Cloud Vault managed leases can either be RENEWED
(if they are renewable) or ROTATED
based on the vault lifecycle configuration.
Sample Vault lifecycle:
vault:
enabled: true
host: 127.0.0.1
port: 8200
scheme: http
uri: http://127.0.0.1:8200/
config:
lifecycle:
min-renewal: 1m
expiry-threshold: 5m
min-renewal
makes sure that the leases are not renewed/rotated too frequently and at least stick around for the configured duration. expiry-threshold
is the configured duration before the lease expiry that vault will renew/rotate a lease.
Spring Cloud Vault and the LeaseContainer will make sure the property sources are updated with the new set of credentials upon a Lease Expiry. However, it is the responsibility of the application to make sure any properties updated under the property sources and environment are propagated through any spring beans initialized with credentials.
Let us assume a spring boot application that managed AWS creds through a ConfigurationProperties class such as below:
Let's also assume that there was another Refresh Scope bean that has an autowired dependency for AwsConfigurationProperties.
In this scenario, it becomes important to listen to SecretLeaseCreatedEvent
and rebind/refresh the respective configuration properties and any other refresh scoped beans within the application that may need updated properties, such as AWS credentials injected. Let us review how we can achieve this next.
- VaultAwsConfiguration shown above registers a lease listener during postConstruct()
- Rebinds any ConfigurationProperties (AwsConfigurationProperties) using a ConfigurationPropertiesRebinder
- Refreshes any refresh scoped beans (AwsConfiguration, basicAWSCredentials, amazonS3Client) on the ApplicationContext upon receiving a SecretLeaseCreatedEvent.
Wondering why not just use spring cloud aws? If you are, you are absolutely right! At the moment, Spring Cloud AWS v2.3.0
only supports AWS access key and secrets. It also does not integrate with vault and lease events at the moment. I do have an issue logged for supporting STS Session Token and it will be a nice addition for Spring Cloud AWS to integrate with Spring Cloud Vault for its credential manager implementation.
Graceful Shutdown
Spring Cloud Vault performs a revoke on any active lease as the application container shuts down. The application will need to configure appropriate permissions on the vault role to perform sys/leases/revoke
so that spring cloud vault could revoke leases.
Something I ran across with Spring Boot 2.4 and legacy bootstrap is that /actuator/refresh
ends up closing the context and thus also triggers destroy() on the LeaseContainer resulting in a revoke. There isn’t a fix or a workaround for this yet under legacy bootstrap, but the recommendation is to cut over to using Config Data API. Note that spring config imports are processed in reverse order. For instance, if we were using multiple sources such as vault and consul (with ACL) for imports, and would like vault secrets to be resolved and imported before others, they will have to be set up as:
spring:
config:
import: consul://,vault://
Unrelated to STS and vault I have a spring boot issue raised for ordered dependency resolution with config data API where we need property sources updated to be honored before we process imports (Such as the consul ACL token from the vault consul backend).
Known Issues
With Spring cloud v2020.0.2
there is a known issue (java.lang.NoSuchMethodError) that stems from spring-cloud-config
due to an incorrect dependency resolution for spring-vault-core
. See Vault core dependency resolution causing java.lang.NoSuchMethodError for more details. This will be corrected in a subsequent release but in the meanwhile, you could implement a workaround to override spring-vault-core
version such as:
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>2.3.2</version>
</dependency>
I hope you enjoyed the read and this helps you on your journey to build secured cloud applications using temporary credentials with AWS STS!
Thank you and stay safe!
Thanks to Attila Vágó, Darragh Grace, and Francislâiny Campos for their feedback on this post!