Fine-Tuning IAM Roles: Selectively Controlling Assumed User Permissions with Conditions

Intro

Say you have an IAM role that multiple users or services assume. An example can be AWS SSO (Identity Center) users that log into AWS - AWS SSO uses sts:AssumeRole API call under the hood to provide temporary credentials to the users. Then, at some point, you realize that you want the role to give slightly broader permissions to some of the users that assume it, but not all of them. For some good reason, you don’t want to create a separate role for that. What do you do? You can use a condition in your role’s IAM policy, and I’m going to show how.

How to match AssumedRole principal in IAM policy condition

First things first, it is important to understand the structure of your assumed caller identity. Start by assuming the role with AWS CLI. If it’s an SSO user, you do aws configure sso to login for the first time or aws sso login --profile <name> if you already logged in before. After you successfully logged in or assumed a role in any other way, e.g. using aws sts assume-role command, you can check your caller identity with aws sts get-caller-identity. It should output something like this:

# aws sts get-caller-identity example output:
{
    "UserId": "AIDACKCEVSQ6C2EXAMPLE:john.doe",
    "Account": "111122223333",
    "Arn": "arn:aws:sts::111122223333:assumed-role/AWSReservedSSO_Developers_604fa8536078x942/john.doe"
} 

What do we see here? There are two attributes that specify the user who assumed the role: UserId and Arn. There are two AWS global condition context keys that might work for us here: aws:PrincipalArn and aws:userid. The ARN seems like the default go-to option, but if you read the description of the aws:PrincipalArn condition key, you’ll find that it won’t work:

For IAM roles, the request context returns the ARN of the role, not the ARN of the user that assumed the role. […] Do not specify the assumed role session ARN as a value for this condition key.

This means that when you assumed a role and make a request to AWS, the PrincipalArn in the request context will not be your assumed role ARN as in the output of get-caller-identity call, but just the ARN of the role, e.g. arn:aws:iam::111122223333:role/some-assumable-role, which isn’t as granular - it doesn’t specify the user who assumed it.

Now that we determined that we cannot use aws:PrincipalArn with assumed role ARNs, our other option is the aws:userid key. This condition key allows you to match the unique id of the assumed role user.

At the first glance at the UserId in the get-caller-identity output it seems that it consists of an AWS secret key id and the username. However, that isn’t a secret key id, but the id of the role that we’ve assumed. You can find an explanation in AWS docs here:

/posts/fine-tuning-assumed-iam-roles/assumed-role-userid.webp
Assumed role UserId
You can verify it by running aws iam get-role --role-name <your-assumabale-role-name> and comparing the role id with the UserId - they should match.

Finally, with this understanding, you can add a condition to your role’s policy like so:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::example-bucket/*",
      "Condition": {
        "StringLike": {
          "aws:userid": [
            "AIDACKCEVSQ6C2EXAMPLE:john.doe",
            "AIDACKCEVSQ6C2EXAMPLE:mary.jane",
            "AIDACKCEVSQ6C2EXAMPLE:aaron.smith"
          ]

Note: you don’t necessarily have to specify the group id. If you want to reuse the policy with other roles you can use a wildcard * for the role id: *:john.doe.

It’s important to point out, that this approach can be considered secure if you have full control over the usernames that come after :, e.g. john.doe, and they are unique. By default, with sts:AssumeRole API call, the callers are free to specify any username (caller-specified-role-name). However, this is not the case with, for example, AWS SSO (Identity Center) users, which makes it safe to use this approach.

Summary

Using conditions in your IAM role’s policy allows you to give specific users or services broader permissions without creating separate roles. By understanding the assumed caller identity structure and using the aws:userid condition key, you can define precise access controls. Ensure that you have control over unique usernames after the : symbol for security. With effective use of conditions, you can optimize IAM roles to meet different user requirements.