Assuming a Role With MFA Using Awsume and the AWS CLI

In this post, we’re going to discuss the process of assuming a role with MFA from the AWS CLI using Awsume. In our last post, we discussed in ad nauseam the process of authenticating to AWS using user credentials and a virtual MFA device. We were able to force MFA and minimize the session by creating a custom policy and then attaching that policy to the user.

There are a couple of problems with the previous approach. First, the process was quite convoluted and difficult to understand and configure. Secondly, user credentials are inherently long-term credentials. We were able to thwart this by implementing a policy requiring the API user to call the get-session-token STS function, which returns a temporary credential. We also forced a maximum limit on that session credential. But what happens if the AWS CLI process is compromised before the session expires?

In AWS, there is no way to invalidate temporary credentials for a user account. Once a temporary credential is granted, it is valid for the remainder of its lifetime. This is why it is so important to create temporary credentials with such a short duration. For most scenarios, you don’t have to worry about this situation, so long as your duration is incredibly short (e.g., 1 hour).

This is a huge deal breaker, though, if your session is ever compromised. You need the ability to revoke a session.

This setup is actually less complicated than before, so I highly recommend using the following approach.

Enter IAM Roles

Roles are quite possibly one of the most misunderstood features in IAM to understand. They aren’t complicated, but they tend to blur the lines between users, groups, and permissions. A role is simply another entity within IAM. You can assign permissions to roles, just like you can users and groups.

The major differentiator between users and roles is that you don’t login as a role. You login as a user, service, or federated account, and then assume a role. When you assume a role, you acquire all of the permissions of that particular role.

In addition, when you assume a role, you acquire temporary credentials and establish a role session. These credentials can be revoked, because you can revoke the session as an administrator. This gives roles a leg up to using user accounts on the command line. If you feel that a session is compromised, you can simply revoke it on the command line and then revoke the privileges for a particular user to assume that role.

Getting Started with a New User

Yet again, we’re going to start with creating a brand new user named ‘cli-user’ in IAM. This time, we’re not going to attach any policies whatsoever to the user. We’re going to proceed to setup a new MFA device for the user, but we will not attach a policy to force the user to use MFA on the command line.

I’m not going to create the user here to keep this post succinct. For information on how to create a new user, see my previous blog post.

Let’s now provision this new user in the AWS CLI with the aws configure command. We can then use Awsume to assume the user’s identity.

$ aws configure --profile cli-user
AWS Access Key ID [None]: <omitted>
AWS Secret Access Key [None]: <omitted>
Default region name [None]: us-east-2
Default output format [None]: json

We can verify that the user can invoke the AWS CLI and call the STS get-caller-identity function. But the user can do nothing else.

$ awsume cli-user
$ aws sts get-caller-identity
{
    "UserId": "<omitted>",
    "Account": "<omitted>",
    "Arn": "arn:aws:iam::<omitted>:user/cli-user"
}
$ aws iam list-roles

An error occurred (AccessDenied) when calling the ListRoles operation:
User: arn:aws:iam::<omitted>:user/cli-user is not authorized to perform:
iam:ListRoles on resource: arn:aws:iam::<omitted>:role/

Creating a New Role

Now we can create a new role in the AWS Management Console. As an administrative user, navigate to IAM and click the Roles link in the sidebar.

AWS Roles

AWS Roles

Click the Create role button. You will see the Create role page. Select Another AWS account and enter your account ID (which can be found under the drop-down menu containing your username in the site header in between the notification bell and the Global dropdown).

Specifying an AWS Account that Can Use the Role

Specifying an AWS Account that Can Use the Role

We do not require an external ID since this will be used by your current AWS account. Do not check the Require MFA option, as we will perform this in a separate step later. Click the Next: Permissions button to continue.

As before, we are going to simulate a CLI account with power user privileges. In the Search text box, type PowerUserAccess and then check the PowerUserAccess policy. Click Next: Tags to continue. Then, click Next: Review to continue.

Attaching a Policy to a Role

Attaching a Policy to a Role

Next name the role and give it a description. I’m going to name mine PowerUserRole. Click the Create role button to create the role.

Naming the Role

Naming the Role

Creating a Policy Granting User Access to the Role

A role exists, but the CLI user cannot yet assume it. We need to create a policy that grants the user permissions to assume the role.

Back in IAM, click the Policies link in the sidebar.

AWS Policies

AWS Policies

On the Create policy page, select the STS service. Select the AssumeRole action.

Creating an AWS Policy to Assume a Role

Creating an AWS Policy to Assume a Role

Select Specific Resource and then click the Add ARN. In the Add ARN(s), type the name of the role that you created in the previous step (mine was PowerUserRole) in the Role name with path * textbox. Then click the Add button.

Adding the Role ARN to the Policy

Adding the Role ARN to the Policy

Select nothing for Request conditions. We’ll deal with MFA later. Then click review Policy to proceed.

Name the new policy and give it a description. I’m going to name mine AssumePowerUserRole. Click the create policy button to create the policy.

Naming the Policy

Naming the Policy

Finally, navigate to the Users page by clicking the Users link in the sidebar. Select our new CLI user. On the Permissions tab, click the Add permissions button. Attach the AssumePowerUserRole policy to the user. Click Next: Review. Finally click Add permissions.

You will most likely want to attach policies at the group level. However, just like in my previous post, I am attaching to the user to simplify the scenario.

Configuring the AWS CLI to Use the New Role

Now we need to provision the new role in the AWS CLI. You can name the role according to your preferences. I will name mine cli-role for brevity. Execute the following commands to add the role. Note that you’ll need your new role’s ARN, which can be found under the Role’s details in IAM.

$ aws configure set role_arn <role-arn> --profile cli-role
$ aws configure set source_profile cli-user --profile cli-role
$ cat .aws/config
[profile cli-user]
region = us-east-2
output = json
[profile cli-role]
role_arn = arn:aws:iam::<omitted>:role/PowerUserRole
source_profile = cli-user

These commands instruct the AWS CLI to configure a new profile for the new role that we created. When a user attempts to login with that profile, the AWS CLI will then attempt to login with the source_profile and then assume the role.

We can see how this works utilizing Awsume to assume the role. Calling Awsume for the cli-user user will not allow us to perform any power user actions. However, calling Awsume for the cli-role will.

$ awsume cli-user
$ aws sts get-caller-identity
{
    "UserId": "<omitted>",
    "Account": "<omitted>",
    "Arn": "arn:aws:iam::<omitted>:user/cli-user"
}

$ aws iam list-roles
An error occurred (AccessDenied) when calling the ListRoles operation:
User: arn:aws:iam::<omitted>:user/cli-user is not authorized to perform:
iam:ListRoles on resource: arn:aws:iam::<omitted>:role/

$ awsume cli-role
[cli-role] Role credentials will expire 2021-02-01 08:10:53
$ aws sts get-caller-identity
{
    "UserId": "<omitted>:cli-role",
    "Account": "<omitted>",
    "Arn": "arn:aws:sts::<omitted>:assumed-role/PowerUserRole/cli-role"
}

$ aws iam list-roles
{
    "Roles": [
    {
        "Path": "/aws-service-role/support.amazonaws.com/",
        "RoleName": "AWSServiceRoleForSupport",
        <...>

Enforcing MFA with Session Duration the New Role

Enforcing MFA and session duration on a role is much more trivial than enforcing it on a user. Back in IAM, select the Roles link in the sidebar and then select the Role from the list of roles.

Activate the Trust relationships tab. Click the Edit trust relationship button. Alter the policy as outlined below:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::520458158213:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "NumericLessThanEquals": {
        "aws:MultiFactorAuthAge": "3600"
        }
      }
    }
  ]
}

The above policy will require anyone authenticating with the specified role to supply an MFA token.

By default, when you create a new role, the maximum session duration is one hour. You can alter this by clicking the Edit button next to the Maximum session duration label on the Summary page of the role. You can choose a minimum of 15 minutes up to a maximum of 36 hours.

Configuring the Role's Maximum Session Duration

Configuring the Role’s Maximum Session Duration

Next, configure the AWS CLI to require MFA for the role. Recall from the previous post that the mfa_serial number value below can be retrieved from the Security credentials tab of the user next to the label “Assigned MFA device”.

$ aws configure set mfa_serial arn:aws:iam::<omitted>:mfa/cli-user --profile cli-role

Now, adjust Awsume’s config.yaml file to account for the role duration:

Note that this step isn’t strictly required. AWS will not provide the smallest session duration between the setting configured above and the role-duration configured in the config.yaml file. I list it here to show that you have the option of specifying a lower value for Awsume.

colors: true
fuzzy-match: false
role-duration: 3600

Assuming a Role Using Awsume

Now that both Awsume and the AWS CLI have been configured, utilizing Awsume to authenticate as the user and assume the role is quite trivial:

$ awsume cli-role
Enter MFA token: <omitted>
Session token will expire at 2021-02-01 19:45:53
[cli-role] Role credentials will expire 2021-02-01 08:45:53

Calling the STS get-caller-identity function yields that we are, in fact, masquerading as our new role:

$ aws sts get-caller-identity
{
    "UserId": "<omitted>:cli-role",
    "Account": "<omitted>",
    "Arn": "arn:aws:sts::<omitted>:assumed-role/PowerUserRole/cli-role"
}

And, as the new role, we can perform Power User functions, such as the IAM list-roles function:

$ aws iam list-roles
{
    "Roles": [
        {
            "Path": "/aws-service-role/support.amazonaws.com/",
            "RoleName": "AWSServiceRoleForSupport",
            <...>

Revoking a Session for Role in AWS Management Console

Now that we have established a session with a role on the AWS CLI, we can revoke this session from the AWS Management Console.

Back in IAM, select the Roles link from the sidebar. Then select the role that we created above. Activate the Revoke sessions tab. Click the Revoke active sessions button.

Note: This button is red for a reason. If you click it, you will effectively kill all sessions for this particular role. Make sure all users and services that assume this role have been informed. Also note that there may be a slight delay before the session is revoked.

Revoking all Sessions for a Role

Revoking all Sessions for a Role

Attempting to do anything with our previously activated session will now yield an access denied.

$ aws iam list-roles

An error occurred (AccessDenied) when calling the ListRoles operation:
User: arn:aws:sts::<omitted>:assumed-role/PowerUserRole/cli-role is not
authorized to perform: iam:ListRoles on resource:
arn:aws:iam::<omitted>:role/ with an explicit deny

Final Thoughts

Though our temporary sessions are short-lived and we now have the ability to revoke them via AWS, there is still one final loose end. Awsume stores the secret access ID and secret key in the .awsume/cache folder. When we are complete with our session, even though we technically can’t “logout,” it is probably wise to remove the MFA secrets from disk.

Disclaimer: I am probably being a bit paranoid here. The cache directory is only readable by the respective logged in user. If a process is running on the machine under that user’s credentials, then that process has access to the cache file and can then perform malicious actions utilizing the temporary credentials. I would certainly like to limit the window as much as possible here.

In short, we can remove the session from the environment variables and from Awsume by executing the following commands:

$ awsume --unset
$ rm $HOME/.awsume/cache/*

 

Photo by Tamara Gak on Unsplash

Leave a Reply