AWS STS with Spring Cloud Vault

Kris Iyer
HMH Engineering
Published in
6 min readMar 30, 2021
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.

IAM user assuming role via STS.
IAM user assuming role via STS.

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:

  1. 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.
  2. assumed_role: Vault will call sts:AssumeRole and return the access key, secret key, and session token to the caller.
  3. 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 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

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 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 is configured for the vault role when using assumed_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:

AwsConfigurationProperties for AWS credentials

Let's also assume that there was another Refresh Scope bean that has an autowired dependency for AwsConfigurationProperties.

AWSConfiguration class with singleton’s for Amazon S3

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 and Lease Listener
  • 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-configdue 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!

Published in HMH Engineering

HMH Engineering builds fantastic software to meet the challenges facing teachers and learners. We enable and support a wide range of next-generation learning experiences, designing and building apps and services used daily by millions of students and educators across the USA.

Responses (1)

What are your thoughts?