avatarTeri Radichel

Summarize

Requiring MFA in an AWS Organizations Service Control Policy

ACM.403 Denying non-role actions without MFA in your organization

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

⚙️ Check out my series on Automating Cybersecurity Metrics. The Code.

🔒 Related Stories: Cloud Governance | IAM | AWS Security

💻 Free Content on Jobs in Cybersecurity | ✉️ Sign up for the Email List

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In the last post I explored limiting a user to changing their own credentials.

In this post, I want to see if we can add the rest of our root policy to an SCP. Let’s take the remaining statements from the policy in the last post one at a time.

The first statement allows some actions without MFA. SCPs are generally for Deny statements. I don’t want to Deny these actions. But let’s check out the next statement.

{
   "Sid": "AllowWithoutMFA",
   "Effect": "Allow",
   "Action": [
    "iam:EnableMFADevice",
    "iam:ListMfaDevices",
    "iam:GetUser",
    "iam:GetAccountPasswordPolicy"
   ],
   "Resource": "arn:aws:iam::xxxxxxxxxxxx:user/${aws:username}"
  }

In the next statement, I’m allowing any action that is not IAM and requires MFA and only in the root account (hopefully — test that if you use it):

{
   "Sid": "AllowAnyActionWithMFAExceptIAM",
   "Effect": "Allow",
   "NotAction": [
    "iam:*"
   ],
   "Resource": "*",
   "Condition": {
    "Bool": {
     "aws:MultiFactorAuthPresent": "true"
    },
    "StringEquals": {
     "aws:PrincipalAccount": [
      "xxxxxxxxxxxx"
     ]
    }
   }
  }

Well we don’t want to allow actions but we can deny actions taken without MFA. I can make a change to the policy to Deny any action that does not meet the MFA condition except the actions allowed if MFA is false.

{
   "Sid": "DenyUserActionsWithoutMFA",
   "Effect": "Deny",
   "NotAction": [
    "iam:EnableMFADevice",
    "iam:ListMfaDevices",
    "iam:GetUser",
    "iam:GetAccountPasswordPolicy"
  ],
  "Condition": { "Bool": { "aws:MultiFactorAuthPresent": "false" } }
}

By the way, I already explained why you should not use IfExists in another post and I really wish AWS would modify the documentation relate to this to be explicitly clear about how this works. I still see documentation recommending this construct in ways that are not entirely clear.

What’s the problem with the above statement? It’s not going to allow any actions taken by roles. Is there a way I can restrict this to actions taken by users?

Well the documentation provides a clue as to how we could make it work but it’s not actually clear:

If the condition keys are missing from a request context, the policy can fail the evaluation.

If the documentation said will fail then we could simply use a condition like:

"Condition": { "StringEquals": { "aws:username": ${aws:username"} }

If the username is in the request the condition would pass because the username always equals itself.

But if a role took the action, the username was not in the request, then the action was not taken by a user and the condition would fail.

But the documentation is not entirely clear on that point as to whether that would actually work.

What if we check to see if the principal ARN is a role:

"Condition": { 
   "ArnNotEquals: { "aws:PrincipalArn": "arn:aws:iam::*:role/*" }
}

So our policy statement becomes:

{
   "Sid": "DenyUserActionsWithoutMFA",
   "Effect": "Deny",
   "NotAction": [
    "iam:EnableMFADevice",
    "iam:ListMfaDevices",
    "iam:GetUser",
    "iam:GetAccountPasswordPolicy",
    "iam:ListUsers"
  ],
  "Condition": {
     "Bool": { "aws:MultiFactorAuthPresent": "false" },
     "ArnNotLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/*" }
  }
}

Update: Note that I had to add ListUsers to the policy above.

Here’s another benefit of the above policy. Any assume role actions should require MFA. That means even the AWS Organizations role which by default does not require MFA to assume should require MFA to assume even though the trust policy does not require it.

What are we possibly missing in the above policy?

Let’s go through the logic:

ARN = Role, No MFA or MFA is false
  MFA = False [ ? ]
  ARN is role [ Fail ]
Result: Condition Fails: Allow

ARN = User, MFA = True
Conditions:
  MFA is false [ Fail ]
  ARN is user [ Pass ]
Result: Condition Fails: Allow

ARN = User, MFA = False
  MFA = False [ Pass ]
  ARN is not role [ Pass ]
Result: Condition is met: Deny

Developer Credentials, ARN = ?, MFA = ?
Result: ?

What if MFA is present but not false? We could have a problem. Does AWS guarantee that when MFA is not present that it will evaluate to false in the above logic? I can’t find documentation stating that is the case.

We can deny requests where the condition key is not present and the ARN is not a role, just to be extra safe.

We should create a separate statement for this purpose. I explained all the logic in the policy construct in the bool with ifexists post above.

{
   "Sid": "DenyUserActionsWithNoMFAKey",
   "Effect": "Deny",
   "NotAction": [
    "iam:EnableMFADevice",
    "iam:ListMfaDevices",
    "iam:GetUser",
    "iam:GetAccountPasswordPolicy",
    "iam:ListUsers"
  ],
  "Condition": {
     #the MultiFactorAuthPresent key is missing
     "Null": { "aws:MultiFactorAuthPresent": "true" },
     "ArnNotLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/*" }
  }
}

Will this work? Will this cause any problems? I don’t know. We’ll need to test it out in future posts. Remember the user I am using to deploy this role is in the root account so is not affected by Service Control Policies. I need to test this with another user in a child account which I will do in a future post.

Update: It works just fine. But really, do I even need all those actions for a null MFA token? Not really. Every action in the console should have the MFA token. So I ended up removing the actions for this statement and deny all actions where the MFA token is missing in the AWS Console. If this doesn’t work somewhere — it’s likely an AWS Bug.

{
   "Sid": "DenyUserActionsWithNoMFAKey",
   "Effect": "Deny",
   "Action": [
    "*"
  ],
  "Condition": {
     #the MultiFactorAuthPresent key is missing
     "Null": { "aws:MultiFactorAuthPresent": "true" },
     "ArnNotLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/*" }
  }
}

I did do a quick check to see if I could view things in the console with a user outside the management account and it appears to be working.

I also was blocked from running my script to make my final changes because I was using an AWS CLI profile that was not configured to use MFA. Shame on me ;-) I need to fix that now. I’ve shown how to use MFA with a CLI role profile in a number of was in prior IAM posts.

There’s a lot more I need to test — like if I can assume roles with MFA and if roles and federated users work properly.

As for the remainder of my rootadmin user policy…

This next statement won’t work because we can’t use the NotResource construct nor can we reference the resource user as explained in the last post.

{
   "Sid": "AllowIAMActionsOnAllButCurrentUser",
   "Effect": "Allow",
   "Action": [
    "iam:AttachUserPolicy",
    "iam:CreateUser",
    "iam:DetachUserPolicy",
    "iam:DeleteUserPermissionsBoundary",
    "iam:GenerateServiceLastAccessedDetails",
    "iam:PutUserPermissionsBoundary",
    "iam:PutUserPolicy",
    "iam:SimulatePrincipalPolicy",
    "iam:Tag*",
    "iam:Untag*",
    "iam:UpdateUser"
   ],
   "NotResource": "arn:aws:iam::xxxxxxxxxxxx:user/${aws:username}",
   "Condition": {
    "Bool": {
     "aws:MultiFactorAuthPresent": "true"
    },
    "StringEquals": {
     "aws:PrincipalAccount": [
      "xxxxxxxxxxxx"
     ]
    }
   }
  }

It seems like we may be able to include this to deny password updates in the console, but I’m not going to include this at the root level since I haven’t resolved the issue in the last post yet. It may be simpler to keep all the policies related to password changes in one policy.

{
   "Sid": "DenyUpdateAndDeleteLoginProfile",
   "Effect": "Deny",
   "Action": [
    "iam:UpdateLoginProfile",
    "iam:DeleteLoginProfile"
   ],
   "Resource": "*"
  }
 ]
}

So the only thing we’re really going to add is to require MFA for all user actions — and we have to test that and make sure it works and doesn’t block something we need to do.

Here’s the full Service Control Policy I’m applying at the root as of now. Note that somehow this policy changed since I first published it. Not sure what is going on but make sure the SIDS have unique names.

Follow for updates.

Teri Radichel | © 2nd Sight Lab 2023

About Teri Radichel:
~~~~~~~~~~~~~~~~~~~~
⭐️ Author: Cybersecurity Books
⭐️ Presentations: Presentations by Teri Radichel
⭐️ Recognition: SANS Award, AWS Security Hero, IANS Faculty
⭐️ Certifications: SANS ~ GSE 240
⭐️ Education: BA Business, Master of Software Engineering, Master of Infosec
⭐️ Company: Penetration Tests, Assessments, Phone Consulting ~ 2nd Sight Lab
Need Help With Cybersecurity, Cloud, or Application Security?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
🔒 Request a penetration test or security assessment
🔒 Schedule a consulting call
🔒 Cybersecurity Speaker for Presentation
Follow for more stories like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
❤️ Sign Up my Medium Email List
❤️ Twitter: @teriradichel
❤️ LinkedIn: https://www.linkedin.com/in/teriradichel
❤️ Mastodon: @teriradichel@infosec.exchange
❤️ Facebook: 2nd Sight Lab
❤️ YouTube: @2ndsightlab
MFA
Service Control Policy
Iam
Governance
Security
Recommended from ReadMedium