Gurjot's Blog

Gurjot's Blog

Securing AWS Infrastructure using MFA

Securing AWS Infrastructure using MFA

You'll learn how you can secure your AWS resources using MFA when being accessed using the AWS Console or AWS CLI.

Featured on Hashnode

Subscribe to my newsletter and never miss my upcoming articles

As security is considered job Zero, the usage of Multi-factor authentication (MFA) within your applications has become more common nowadays. MFA is simply a security best practice that adds an extra layer of protection on top of your user name and password.

Now coming to AWS, resources are usually accessed using the AWS Console or they are accessed programmatically (which includes CLI/SDK/API). So, in this guide we'll learn about how can we secure our AWS infrastructure using MFA when used via AWS Console or CLI (where a similar approach can be applied for API/SDK).

MFA for AWS Console

The access to AWS Console is generally provided by creating an IAM user. The IAM user when created without any MFA configuration can access the AWS console just using a username and password.

1 copy.png

Configuring MFA for an IAM user enforce the user to provide a MFA code during signing-in into the AWS console.

As an example, let's create an IAM user in AWS and configure a MFA device for that user:

1) Login to the AWS Management Console and visit the IAM Console

2) Choose Users in the navigation pane and click "Add users" to create a new IAM user.

2.png

3) Enter the user details and click "Next: Permissions".

3.png

4) Select the permissions that you want to assign to your IAM user. For example, I have attached the "ReadOnlyAccess" AWS Managed policy which provides read-only access to all the AWS services. Now, choose "Next: Tags".

4.png

5) (Optional) Add metadata to the user by attaching tags as key-value pairs. Then, click "Next: Review". Now, review the user details and click "Create User".

5.png

Once the user is created, go back to the Users section and select the "MFA-User" to view the user's summary. Now we're going to configure the MFA device for this user.

a) Click "Manage" to assign a MFA device to this user.

6 copy.png

b) In the Manage MFA Device wizard, choose Virtual MFA device, and then choose Continue.

7.png

The Virtual MFA device option allows to configure a MFA device using apps (such as, Google Authenticator or Authy) in our mobile devices.

In case you want to configure a different type of MFA device, you can refer to the AWS documentation here

c) Open the virtual MFA app on your mobile. For a list of apps that we can use for hosting virtual MFA devices, click here.

8.png

Now scan the QR code as shown above. MFA app will now start generating the MFA codes. If the QR code is not supported, you can type the secret key in the MFA app as an alternative.

d) Input the two consecutive MFA codes generated by the app and click "Assign MFA".

Finally, the MFA device is now configured for the IAM user. The user will now be prompted to input a MFA code on each login.

⚡️Interesting Observation⚡️

Try using the AWS CLI to access any AWS service using the credentials of the "MFA-User" and you will observe that the resources can be accessed without MFA. Weird, huh !!

Let's understand that, a key named "MultiFactorAuthPresent" with a boolean value is included when a request is made to an AWS service and tells if MFA is used or not. This key is included in the request context only when the principal uses temporary credentials to make the request. The key is not present in AWS CLI, AWS API, or AWS SDK requests that are made using long-term credentials (i.e. AccessKey & SecretAccessKey). So, this is the reason we are able to access the AWS services without MFA in AWS CLI.

Note: IAM users in the AWS Console unknowingly use temporary credentials i.e. after signing in using the username, password (which are long-term credentials) and MFA code, the console generates temporary credentials on behalf of the user in the background with the key "MultiFactorAuthPresent" set to "true". These temporary credentials are then used to make all the requests to AWS while you are using the console.

We can verify the above information using a simple fact that we are logged out of the console automatically after 12 hours. This is because it was using temporary credentials which got expired.

The question now comes up is that if the key "MultiFactorAuthPresent" is not present in the requests made using AWS CLI (which is using long term credentials), then how can we enforce MFA for CLI. Any thoughts on that? Let's discuss that next...

MFA for AWS CLI (or SDK or API)

As the "MultiFactorAuthPresent" key is not present, AWS cannot perform any check for this key. Hence, the control is now shifted to our hands. But how can we control access to our AWS resources. Any guesses???

Yes, that's right our own IAM policies. These can be used to enforce MFA for the requests coming from AWS CLI. Instead of checking the value of the key "MultiFactorAuthPresent", we will check if this key is actually present in the request or not. If not, that means the user is using long term credentials which are not MFA authenticated. Based on that evaluation we can then allow or deny access to the AWS services.

The following example IAM policy can be attached to your IAM users, which will enforce users to use MFA to access any AWS services:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "BlockMostAccessUnlessSignedInWithMFA",
            "Effect": "Deny",
            "NotAction": [
                "iam:CreateVirtualMFADevice",
                "iam:DeleteVirtualMFADevice",
                "iam:ListVirtualMFADevices",
                "iam:EnableMFADevice",
                "iam:ResyncMFADevice",
                "iam:ListAccountAliases",
                "iam:ListUsers",
                "iam:ListSSHPublicKeys",
                "iam:ListAccessKeys",
                "iam:ListServiceSpecificCredentials",
                "iam:ListMFADevices",
                "iam:GetAccountSummary",
                "sts:GetSessionToken"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}

The "BoolIfExists" condition operator in the above policy plays the main role here. It checks if the "MultiFactorAuthPresent" key is present in the request.

  • If the "MultiFactorAuthPresent" key is not present, IfExists evaluates the condition element as true. So, based on the policy every action except the one's mentioned in the "NotAction" element is denied. The allowed actions above provide the user with minimum permission to configure a MFA device for itself and to get temporary credentials which are MFA authenticated.
  • If the "MultiFactorAuthPresent" key is present, the condition is further evaluated to check if it's value is true or false. In case the value is "true", the permissions are granted to access the AWS resources based on other attached IAM policies. If "false", the user then only has minimum permissions to configure a MFA device for itself and to get temporary credentials which are MFA authenticated.

Hence, applying the above policy to our "MFA-User" has now added the additional layer of security for programmatic access too i.e. while using CLI/SDK/API you must now use MFA authenticated temporary credentials to access any AWS resource.

What's next 🤔

The only detail now left is that how can we make our requests coming from AWS CLI/SDK/API MFA authenticated. We will discuss that in the next post i.e. how can we use our long term credentials to generate MFA authenticated temporary credentials to access resources using CLI. We will also take a look at a script to automate this task, as getting MFA credentials manually after they get expire can be a bit tiring.


👋 Enjoyed the article? Reach out to me anytime in the comments below or on Twitter for any further discussion or feedback.

 
Share this