You're reading for free via Karanbir Singh's Friend Link. Become a member to access the best of Medium.

Member-only story

Spring Boot + AWS Roles Anywhere: Integration

Karanbir Singh
5 min readOct 25, 2024

AWS roles anywhere is an awesome IAM service particularly for the apps which are non AWS, or for apps that are complex in nature(even if deployed on AWS) where role based access is not available directly in an easy option.

If not a medium member, try using the link here to view post for free.

Please follow and like the post, if it has helped you by any way.

Continuing from the previous post about AWS roles anywhere here, this post focuses majorly on the programmatic access credential generation based on AWS roles anywhere, without the need of external utility of credential helper. There had been ample amount of community posts and forums online with similar needs which I intend to address here.

Required Knowledge

➫ AWS Basics, AWS IAM most importantly
➫ AWS roles anywhere some knowledge
➫ Must have knowledge on PKI — needed for certificate generation etc.
➫ Average knowledge on Spring & Spring Boot + Java.
➫ Local setup of any IDE along with Maven + JDK 17+.
➫ Setup of openssl or something like step cli.

Out of Scope

➫ AWS IAM roles anywhere setup is not covered in the post.

The integration intention as flow diagram

I know the diagram is not clear, you can skip to understand it right away.

In nutshell the intention is to Access the AWS resource(AWS S3 as part of the example code) using the X509 certificate based credentials whose CA is trusted in AWS IAM roles anywhere trust.

Flow of the Signing Process

All of this for Authorization with the AWS’s roles anywhere sessions endpoint.

There are 4 major steps needed for generating the Authorization header.

Step 1 Create Canonical Request & Hash it

Bytes of the canonical request are encoded as UTF-8 ⇢ hashed with SHA-256 ⇢ the resulting bytes are hex encoded ⇢ and lowercased

Hashing looks straight forward! 🥹

But structure of Canonical Request String itself is very complex and error prone:-

Step 2 Prepare String to be signed

Step 3 ➫ Generate Signature

Generating Signature involves the details in the image below:-

Step 4 ➫ Create Authorization Header

Structure of the value of the Authorization header is as below(details are part of the image):-

Authorization: {Algorithm} Credential={CredentialString}, SignedHeaders={SignedHeaders}, Signature={Signature}

The value generated above will be used as Authorization Header value.

Introduction to code

Whatever code I did primarily has Spring flavour but it can be easily ported over to other JVM based libraries and frameworks.

Best part it can be passed to almost all resource based clients like S3, DynamoDB, etc.

Additionally beauty of the piece of code is that it works in a great way to fetch new temporary AWS credentials before they are actually expired. Again it heavily inspired by AWS’s JAVA SDK v2.

Quick look through the important components in the code.

Credential provider

One of the main parts of the code. It initializes a custom IAMRolesAnywhereSessionsCredentialsProvider ➫ It is inspired greatly from the AWS’s JAVA SDK v2 & particularly it was motivated from the class StsGetSessionTokenCredentialsProvider & it’s parent StsCredentialsProvider.

The main properties it requires are:-

RoleARN ➫ The role against which we want to get AWS credentials
ProfileARN ➫ The AWS IAM Roles Anywhere Profile!
TrustAnchorArn ➫ ARN of the trust anchor.
Base64 Encoded Private Key ➫ The private key relating to app/ client certificate.
Base64 Encoded Certificate ➫ The clientcertificate itself.
Duration Seconds ➫ The expiration is binding to this property.
Region ➫ AWS region. It has to be same as the profile and trust anchor’s region

config properties configured in src/main/resources/application.properties

aws.account.id=111111111111
aws.roles.anywhere.region=us-east-1
aws.roles.anywhere.role-arn=arn:aws:iam::${aws.account.id}:role/ROLES_ANYWHERE_S3_READ_ONLY
aws.roles.anywhere.profile-arn=arn:aws:rolesanywhere:us-east-1:${aws.account.id}:profile/a-random-long-id
aws.roles.anywhere.trust-anchor-arn=arn:aws:rolesanywhere:us-east-1:${aws.account.id}:trust-anchor/a-random-long-id
aws.roles.anywhere.duration-seconds=900
aws.roles.anywhere.encoded-private-key=removed for security and brevity
aws.roles.anywhere.encoded-x509-certificate=removed for security and brevity

To initiate the client provider there are two variants, example can be seen in the AWSConfig file. Passed the credentials provider as anyone would generally do. Example code:-

@Configuration
public class AwsConfig {

@Bean
public AwsCredentialsProvider awsCredentialsProvider(final AwsRolesAnywhereProperties awsRolesAnywhereProperties,
final ObjectMapper objectMapper)
{
var rolesAnywhereCredentialsProvider = new IAMRolesAnywhereSessionsCredentialsProvider
.Builder(awsRolesAnywhereProperties, objectMapper)
.asyncCredentialUpdateEnabled(true)
.build();
return rolesAnywhereCredentialsProvider;
}

@Bean
public AwsCredentialsProvider awsCredentialsProviderV2(final AwsRolesAnywhereProperties awsRolesAnywhereProperties,
final ObjectMapper objectMapper)
{
var rolesAnywhereCredentialsProvider = new IAMRolesAnywhereSessionsCredentialsProvider
.Builder(objectMapper)
.roleArn(awsRolesAnywhereProperties.getRoleArn())
.profileArn(awsRolesAnywhereProperties.getProfileArn())
.trustAnchorArn(awsRolesAnywhereProperties.getTrustAnchorArn())
.encodedPrivateKey(awsRolesAnywhereProperties.getEncodedPrivateKey())
.encodedX509Certificate(awsRolesAnywhereProperties.getEncodedX509Certificate())
.durationSeconds(awsRolesAnywhereProperties.getDurationSeconds())
.region(awsRolesAnywhereProperties.getRegion())
.asyncCredentialUpdateEnabled(true)
.prefetch(true)
.build();
return rolesAnywhereCredentialsProvider;
}

// pass the credentials provider as anyone would generally do
@Bean
S3Client s3Client(final AwsCredentialsProvider awsCredentialsProvider,
final AwsRolesAnywhereProperties awsRolesAnywhereProperties)
{
return S3Client.builder().credentialsProvider(awsCredentialsProvider).region(Region.of(awsRolesAnywhereProperties.getRegion())).build();
}

}

Underneath there are two core Util classes which exposes good number of internal and external static functions.

AwsX509SigningHelper ➫ generates and manipulates the request body creation, etc. as part of the AWS Authorization Signing Process, as described here as on October 2024.

CertAndKeyParserAndLoader ➫ manages certificate and key related utility functions

The code repository is located herehttps://github.com/krnbr/aws-iam-rolesanywhere. The main branch makes use of the Spring’s Rest Client!

But for example of not Spring’s Rest Client usage ➪ refer the apache-client branch at https://github.com/krnbr/aws-iam-rolesanywhere/tree/apache-client

Karanbir Singh
Karanbir Singh

Written by Karanbir Singh

API developer + Web Application developer + Devops Engineer = Full Stack Developer

No responses yet

Write a response