enforce MFA for AWS console login, but not for API calls

user2131779 picture user2131779 · Jan 27, 2015 · Viewed 8.7k times · Source

I am looking to enforce all IAM users(local and remote) to enable and activate their MFA devices. I want them all to enable MFA to do their respective tasks.

I am trying with the following policy

{
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*",
      "Condition": {"Bool": {"aws:MultiFactorAuthPresent": "true"}}
}

However; this policy applies irrespective of how you are accessing the services, through console or through APIs

There is a lot of automation done by all users and their automation breaks as MFA authentication was not implied.

As a first step, we wish everybody to atleast enables MFA for console login; but the same should not enforce them to use MFA for API calls used in automation.

Is this achievable through IAM policy?

Thanks

Answer

Josh Hancock picture Josh Hancock · Dec 9, 2016

The trick is to reverse the check...rather than only allowing if aws:MultiFactorAuthPresent is true, deny if it's false.

Here's the doc on self-service MFA management: http://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_users-self-manage-mfa-and-creds.html

The full policy suggested in there is:

{
    "Version": "2012-10-17",
    "Statement":[
        {
            "Sid": "AllowAllUsersToListAccounts",
            "Effect": "Allow",
            "Action":[
                "iam:ListAccountAliases",
                "iam:ListUsers",
                "iam:GetAccountSummary"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowIndividualUserToSeeAndManageTheirOwnAccountInformation",
            "Effect": "Allow",
            "Action":[
                "iam:ChangePassword",
                "iam:CreateAccessKey",
                "iam:CreateLoginProfile",
                "iam:DeleteAccessKey",
                "iam:DeleteLoginProfile",
                "iam:GetAccountPasswordPolicy",
                "iam:GetLoginProfile",
                "iam:ListAccessKeys",
                "iam:UpdateAccessKey",
                "iam:UpdateLoginProfile",
                "iam:ListSigningCertificates",
                "iam:DeleteSigningCertificate",
                "iam:UpdateSigningCertificate",
                "iam:UploadSigningCertificate",
                "iam:ListSSHPublicKeys",
                "iam:GetSSHPublicKey",
                "iam:DeleteSSHPublicKey",
                "iam:UpdateSSHPublicKey",
                "iam:UploadSSHPublicKey"
            ],
            "Resource": "arn:aws:iam::accountid:user/${aws:username}"
        },
        {
            "Sid": "AllowIndividualUserToListTheirOwnMFA",
            "Effect": "Allow",
            "Action":[
                "iam:ListVirtualMFADevices",
                "iam:ListMFADevices"
            ],
            "Resource":[
                "arn:aws:iam::accountid:mfa/*",
                "arn:aws:iam::accountid:user/${aws:username}"
            ]
        },
        {
            "Sid": "AllowIndividualUserToManageTheirOwnMFA",
            "Effect": "Allow",
            "Action":[
                "iam:CreateVirtualMFADevice",
                "iam:DeactivateMFADevice",
                "iam:DeleteVirtualMFADevice",
                "iam:RequestSmsMfaRegistration",
                "iam:FinalizeSmsMfaRegistration",
                "iam:EnableMFADevice",
                "iam:ResyncMFADevice"
            ],
            "Resource":[
                "arn:aws:iam::accountid:mfa/${aws:username}",
                "arn:aws:iam::accountid:user/${aws:username}"
            ]
        },
        {
            "Sid": "BlockAnyAccessOtherThanAboveUnlessSignedInWithMFA",
            "Effect": "Deny",
            "NotAction": "iam:*",
            "Resource": "*",
            "Condition":{
                "BoolIfExists":{ "aws:MultiFactorAuthPresent": "false"}
            }
        }
    ]
}

The most important part is the last statement, which does the deny. If you change it to this:

{
    "Sid": "BlockAnyAccessOtherThanAboveUnlessSignedInWithMFA",
    "Effect": "Deny",
    "NotAction": "iam:*",
    "Resource": "*",
    "Condition":{
        "Bool":{ "aws:MultiFactorAuthPresent": "false"}
    }
}

(BoolIfExists changed to Bool) it will allow IAM access keys to bypass the requirement of MFA, while still requiring you to use MFA when logging in through the AWS Console.

Be careful if you decide to use that full policy from the docs. Note that it allows a user to create access keys and change their password, and the deny clause only blocks non-IAM actions...this means that, if MFA gets disabled on an account, a user's password could be changed or new access keys could be provisioned without an MFA check, and if you've made the Bool change, those new access keys would be able to access anything that the user has permissions for, without MFA. I.E., all of the security vulnerabilities of unsecured keys, with some potential for account hijacking on top.

I would suggest using a policy similar to this instead:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowAllUsersToListAccounts",
            "Effect": "Allow",
            "Action": [
                "iam:ListAccountAliases",
                "iam:ListUsers"
            ],
            "Resource": [
                "arn:aws:iam::accountid:user/*"
            ]
        },
        {
            "Sid": "AllowIndividualUserToSeeTheirAccountInformation",
            "Effect": "Allow",
            "Action": [
                "iam:GetAccountPasswordPolicy",
                "iam:GetAccountSummary",
                "iam:GetLoginProfile"
            ],
            "Resource": [
                "arn:aws:iam::accountid:user/${aws:username}"
            ]
        },
        {
            "Sid": "AllowIndividualUserToListTheirMFA",
            "Effect": "Allow",
            "Action": [
                "iam:ListVirtualMFADevices",
                "iam:ListMFADevices"
            ],
            "Resource": [
                "arn:aws:iam::accountid:mfa/*",
                "arn:aws:iam::accountid:user/${aws:username}"
            ]
        },
        {
            "Sid": "AllowIndividualUserToManageThierMFA",
            "Effect": "Allow",
            "Action": [
                "iam:CreateVirtualMFADevice",
                "iam:DeactivateMFADevice",
                "iam:DeleteVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:ResyncMFADevice"
            ],
            "Resource": [
                "arn:aws:iam::accountid:mfa/${aws:username}",
                "arn:aws:iam::accountid:user/${aws:username}"
            ]
        },
        {
            "Sid": "DoNotAllowAnythingOtherThanAboveUnlessMFAd",
            "Effect": "Deny",
            "NotAction": "iam:*",
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}