Assuming Roles with IAM Roles Anywhere

Introduction

We recently came into a situation where we wanted to deploy a CDK application into an AWS environment from a source outside of AWS but we did not want to store Secret Access Keys directly on the outside service needing the connection – even if they were stored as a secret with no read access and encrypted at rest. To complete the deployment we needed the AWS CLI and AWS CDK Toolkit.

To establish the authentication, we used AWS IAM Roles Anywhere, but we needed a secondary permission to allow assuming the CDK IAM Roles created via CDK Bootstrap.

What is IAM Roles Anywhere?

IAM Roles Anywhere is a relatively new AWS service that allows temporary security credentials in IAM for servers / applications that run outside of AWS. It allows the outside service to interact with your AWS environment and abide by the permissions via IAM in the same way IAM is used within the AWS environment.

IAM Roles Anywhere uses X.509 certificates issued by a certificate authority (CA) to establish trust between your public key infrastructure (PKI) and AWS IAM Roles Anywhere.

Essentially it allows the expansion of AWS IAM permissions without being tied to a user onto compute not in an AWS environment. For further information, this blog post provides a great example of IAM Roles Anywhere in use and how certificate common name can be used as a condition to restrict access.

Why use IAM Roles Anywhere?

IAM Roles Anywhere is a great alternative method to authenticating with AWS in situations where OIDC is not available.

The following points lead to the decision to use IAM Roles Anywhere:

  • The ability to control how long the authenticated session is valid for eg. 15 minutes.
  • Tracing of session activity in CloudTrail due to the`sts:setSourceIdentity` and `sts:TagSession` being applied to the role and roles being assumed.
  • We wanted least privilege access, so we were okay with the functionality limitation of the IAM Anywhere Role being scoped down to a single AWS account.
  • There was existing public key infrastructure (PKI) available to use with Certificate Authority (CA) already used internally, however an AWS CA can be created if no CA exists.

Adding the Permissions on roles you want to assume

For this example we are going to reference the following roles:

  • The initial IAM Roles Anywhere Role called “cdk-roles-anywhere-role”
  • The target role we want to assume which will be one of the CDK Bootstrap roles eg “cdk-lookup-role”

When creating the IAM Roles Anywhere initial role “cdk-roles-anywhere-role”, the trust relationship has the following policy:

“Effect”: “Allow”,
        “Principal”: {
            “Service”: [
                “rolesanywhere.amazonaws.com”
            ]
          },
          “Action”: [
            “sts:AssumeRole”,
            “sts:TagSession”,
            “sts:SetSourceIdentity”
          ],

Where we can see the `sts:TagSession` and `sts:SetSourceIdentity` is added alongside the `sts:AssumeRole`.

For the “cdk-roles-anywhere-role” role to assume the “cdk-lookup-role”, the “cdk-lookup-role” needs the `Trust Relationship` updated to include the `sts:TagSession` and `sts:SetSourceIdentity` in addition to the usual `sts:AssumeRole` that you would expect.

Failure to do so will return the following error –

An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::[AccountID]:assumed-role/[InitialIamRole]/01 is not authorized to perform: sts:SetSourceIdentity on resource: arn:aws:iam::[AccountID]:role/[TargetIamRole]

or alternatively:

An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::[AccountID]:assumed-role/[InitialIamRole]/01 is not authorized to perform: sts:TagSession on resource: arn:aws:iam::[AccountID]:role/[TargetIamRole]

This depends on the current state of the trust policy of the target IAM role.

For our use case we modified the standard CDK Bootstrap CloudFormation template.

so that the CDK Bootstrap roles created had the `sts:TagSession` and `sts:SetSourceIdentity` added (except for the CloudFormationExecutionRole) and we could run the native `cdk synth` and `cdk deploy` command from the external compute and the CDK roles were correctly assumed under the hood to complete the deployment.

The CDK IAM Role Trust relationships looked like this after the update:

{
“Version”: “2008-10-17”,
“Statement”: [
{
  “Effect”: “Allow”,
  “Principal”: {
“AWS”: “arn:aws:iam::[AccountID]:root”
  },
“Action”: [
  “sts:AssumeRole”,
  “sts:TagSession”, // new addition
  “sts:SetSourceIdentity” // new addition
  ]
    }
  ]
}

Summary

Being able to authenticate and leverage the AWS IAM ecosystem from compute outside the AWS environment is extremely powerful functionality.

This example talks about assuming roles to perform CDK deployments but the ability to assume roles allows compute outside of AWS to adopt the permissions like it exists inside AWS and perform the same functions like allowing read and write permissions with an S3 bucket or interacting with API Gateway.

Additional Notes

  • When we updated the CDK Bootstrap roles we did not update the CloudFormationExecutionRole as that should only be accessed by the other CDK Bootstrap roles and not end users.
  • The `sts:SetSourceIdentity` is an excellent feature of AWS and helps track end to end what users are doing as they assume different roles via the CLI or API. The `sts:SetSourceIdentity` action cannot be used with roles intended to be used via the AWS Management Console
  • The intended roles to be assumed by Iam Roles Anywhere should be strictly “robot” roles i.e. roles not intended for humans to assume due to the limitation mentioned above
  • We have a GitHub repository available to use which has the references to the final CloudFormation templates used to create the CDK Bootstrap and IAM Roles Anywhere resources referenced in this blog post.

Enjoyed this blog?

Share it with your network!

Move faster with confidence