Implementing MFA for AWS CLI
Multi Factor Authentication. It can be a major blessing and a massive inconvenience all at the same time. Security or convenience, which one do you want?
Let me preface this article with the fact that our AWS environment has just a few amount of users that utilize AWS CLI for completing tasks, so that made it a bit easier for us. Also, this assumes you already have a basic IAM setup in place. Your users should have the correct permissions, belong in their respective groups, etc. A final note is that it only provides temporary STS based credentials that are valid for an hour (which is the default). For most use cases this is fine, but for long running jobs it may become an issue. So, with all that being said, let’s go ahead and break down how we set it all up.
First things first, pseudo code…well sort of. We went ahead and brainstormed on how to accomplish what we needed, then we wrote it out on a whiteboard. It was pretty simple, we wanted users to utilize their default IAM creds and then require MFA for all actions. This seemed to be the most efficient approach. To accomplish this we had users assume a role within IAM that contains the same privileges as their normal account.
So, the first action step was to create the IAM role. When creating the new role, we selected the ‘Role for cross-account access’ option from the role type page. You then enter your own ‘Account ID’ and check the ‘Require MFA”. We then attached the same managed policy as the users had before (Example: PowerUserAccess). After the role was created and the policy was attached we manually edited the Trust Relationships; each user that would need access was added to the policy. We removed the default entry that is provided as this allows ALL users to assume the role, we don’t want that. Within that trust relationship policy document was the boolean ‘Condition’ that required the MFA Boolean to be True. If it was False, then it’s a no go! Maybe next time…or probably not. :(
See boolean condition JSON format below:
Next was the managed policy creation. We created a new IAM policy to allow the desired IAM users to assume the newly created role mentioned in the previous step. The policy was then attached to the IAM group that the users belonged to. It also included an MFA section that would “Deny All” actions if no MFA was present. See said policy below:
With those steps being done, that meant all AWS sided aspects of the solution were taken care of. We then moved onto the local user’s workstations. Everyone already had their AWS CLI installed and configured with their credentials. This meant we just had to make a few tweaks to a single file to get things going. The lucky file that required a tweak was ~/.aws/config
We went in and added the following bit of info to the file (example below):
- role_arn = ROLE_ARN (found in IAM)
- mfa_serial = Assigned MFA Device (Under user’s security credentials tab)
- source_profile = default
That’s all it took! Now whenever a user would attempt to perform an AWS CLI command, if they did not configure the MFA setup on their end they would receive an Access Denied message. Seen below:
If the user followed directions and set everything up how it should have been done, they would simply be prompted for an MFA code. This MFA code is retrieved from their original MFA device. Simple as that! Now the users have to initially use MFA in order to call AWS CLI commands successfully. An example of correct setup is below:
So, for a TL;DR recap. We created a new role with identical permissions to the user’s current permissions. Then, we allowed specific users to assume this role using STS while also requiring MFA to do so. A custom manged policy was created next and assigned to the user’s targeted group. This policy restricts access to be able to assume said role from above and denies all non MFA approved requests. The last touch was to edit the local config file for each user to their specific needs and then we were off to the races!
I hope this was easy to follow and understand. I figured I had to sort of do this form scratch, so I might as well document it in case someone else needs something similar in the future. Thanks for reading and please share your input as this is my first published article!