Cyberattacks thrive and Multi-Factor Authentication (MFA) is a best practice that protects all kinds of online accounts from hacking attempts. MFA requires a second device that will output a confirmation code required to enable a sensitive action such as login. The key reasoning behind MFA is that one of your devices may be hacked, but all of them at once is less probable. Amazon Web Services (AWS) accounts are among the most valuable that can be: You don’t want to give control to your most precious IT assets just because your main laptop was hacked, do you?
Enabling and using MFA within the AWS Management Console is relatively easy and requires 8 simple steps, according to AWS documentation. However, setting up MFA that way is limited to the AWS Console accesses. If you use any Command-Line Interface (CLI) or APIs, the access keys will not be covered by MFA. Sadly, securing those accesses is much more complex. In this article, we explain how to use awless
to simplify the usage of MFA for AWS users within an organization. awless
is a powerful open-source CLI for AWS; The project was released in February 2017 and quickly gained traction, with more than 3.7k stars on GitHub and praise from key AWS evangelists.
Enabling MFA in just 2 steps
With awless
, it is very simple to enable MFA with a Virtual MFA device: A software application generating a time-based unique password, generally running on a smartphone. For example: Google Authenticator [iOS, Android], Authy 2-Factor Authentication [iOS, Android], or the open source FreeOTP [iOS, Android, F-droid].
Let’s start by creating a device:
> awless create mfadevice name=my-user-name
The command above will generate a QR code nicely displayed in the CLI that you can directly scan in your Virtual MFA app as shown in the following example:
Now that we have a MFA device, we need to attach it to a given user, by running the following command and entering two consecutive codes generated by the Virtual MFA:
> awless attach mfadevice
Please specify (Ctrl+C to quit, Tab for completion):
mfadevice.id? [TAB]
mfadevice.id? arn:aws:iam::0123456789:mfa/my-user-name
mfadevice.mfa-code-1? 180492
mfadevice.mfa-code-2? 240185
mfadevice.user? my-user-name
...
Do you want to create a profile for this MFA device in /home/me/.aws/config? [y/N] n
Thanks to awless
, it took us just two steps to activate the MFA for the AWS Console. But beware: CLI/API calls are still performed without MFA and since you use awless
, you’d rather use the CLI.
Note two things: (1) You need a few permissions to run the command above: iam:CreateVirtualMFADevice
and iam:EnableMFADevice
. Don’t worry if you don’t have them, we will cover this later. (2) AWS recommends to use the same name for the user and the virtual device, as a user can only be attached to a single MFA device.
In the rest of this article, we will expand the protection to API calls and CLI use. To achieve that, we will create roles with high privileges that can be assumed only when being MFA-authenticated, then demote the privileges given to the actual users authenticating with AWS credentials.
Creating groups, roles and policies without editing a single line of JSON
Imagine we are a super administrator (in group superAdmin
) of a large AWS account and we want to enforce MFA for sensitive actions performed through AWS API calls. Not all actions are equal: We want to allow read-only access (for instance listing users with awless list users
) without MFA but we want to enforce the use of MFA to create a new user (awless create user
).
Technically, we will introduce the policies in the following figure to distinguish between two roles:
- For a request without MFA, the CLI uses the user’s credentials directly and rely on the
IAMReadOnlyAccess
policy attached to members of thesuperAdmin
group. - For a request with MFA, the CLI requires a valid MFA authentication code. Then it uses the
CanAssumeSuperAdminMFA
policy from thesuperAdmin
group to assume thesuperAdminWithMFA
role. On AWS, the trust policy of thesuperAdminWithMFA
role checks the user is MFA-authenticated before giving temporary credentials. With these new credentials, assuming thesuperAdminWithMFA
role, the user hasIAMFullAccess
permissions.
The following figure shows an overview of the policies attached to the superAdmin
group and the corresponding role (superAdminWithMFA)
and how they are used with (green) or without (orange) MFA.
Policy for your own virtual MFA device
With the following awless template, let’s create the policy recommended by AWS to allow each user to manage its own MFA device (create a device, attaching the device, etc.).
Run this template directly from github or store it locally (ManageOwnMFA.aws
) and run it with:
> awless run ManageOwnMFA.aws account.id=$(awless whoami --account-only)
(Note that we inline here the use of awless whoami --account-only
to get the AWS account ID.)
Groups and corresponding roles
Following AWS best practices, we will use groups and roles to give permissions to users. As presented before, we have a superAdmin
group:
> awless create group name=superAdmin
and a role that admins will assume when using MFA:
> awless create role name=superAdminWithMFA conditions=\"aws:MultiFactorAuthPresent==true\" principal-account=$(awless whoami --account-only)
(Note that the trust policy of the role requires the users to be MFA-authenticated in order to assume the superAdminWithMFA
role thanks to the aws:MultiFactorAuthPresent==true
condition.)
We create the policy allowing to assume the role:
> awless create policy name=CanAssumeSuperAdminMFA action=sts:AssumeRole effect=allow resource=arn:aws:iam::$(awless whoami --account-only):role/superAdminWithMFA
Super administrator’s permissions
The last step is to attach the policies to the appropriate groups or role.
We want users in the superAdmin
group to be able to:
- manage their own MFA devices
> awless attach policy group=superAdmin arn=arn:aws:iam::$(awless whoami --account-only):policy/ManageOwnMFADevice
- assume the
superAdminWithMFA
role, if they are MFA-authenticated
> awless attach policy group=superAdmin arn=arn:aws:iam::$(awless whoami --account-only):policy/CanAssumeSuperAdminMFA
- have a read-only access to IAM, even without MFA
> awless attach policy group=superAdmin service=iam access=readonly
We want the superAdmin
users being MFA-authenticated, assuming the superAdminWithMFA
role to be able to:
- have full access to IAM
> awless attach policy role=superAdminWithMFA service=iam access=full
Registering a new employee in a flash
Now that we have built the policies, groups and roles for our users, it is very easy to register a new user, while enforcing MFA. For example, to create a superAdmin
named janedoe
, run:
> awless create user name=janedoe
> awless attach user name=janedoe group=superAdmin
Generate access keys for CLI/API accesses with
> awless create accesskey user=janedoe
Transmit (ex: using a flash drive) the sensitive credentials to the new user.
If you also want to give access to the AWS Web console, run:
> awless create loginprofile username=janedoe password=PutHereATemporaryPassword password-reset=true
(Note that if you want to automate this process even more, you can create easily create an awless template for user creation.)
For now, the only permissions granted to the newly created user janedoe
is to make read-only operations on IAM and create and attach a MFA device for herself. Thus, as detailed at the beginning of this article, janedoe
can run herself:
> awless create mfadevice name=janedoe
> awless attach mfadevice
Please specify (Ctrl+C to quit, Tab for completion):
mfadevice.id? [TAB]
mfadevice.id? arn:aws:iam::0123456789:mfa/janedoe
mfadevice.mfa-code-1? 180492
mfadevice.mfa-code-2? 240185
mfadevice.user? my-user-name
...
Do you want to create a profile for this MFA device in /home/me/.aws/config? [y/N] y
Role name or ARN to assume with this MFA device ?
> [TAB]superAdminWithMFA
Enter source profile used to assume role: (default) [Enter]
Enter new MFA profile name: (mfa) [Enter]
...
append to '/Users/jane/.aws/config'? [y/N] y
Now, janedoe
can use the mfa
profile to assume the superAdminWithMFA
role and be authorized to make read-write API calls to IAM, for example:
> awless create user name=otheruser -p mfa
And that’s it! janedoe
has sufficient permissions to make read-only requests without MFA. After creating her MFA device, she can use it for full-access to APIs.
Installing awless
Ready to try it yourself?
awless
is available through:
- pre-built binaries for Linux/macOS/Windows
- Homebrew packages for macOS:
brew tap wallix/awless; brew install awless
curl https://raw.githubusercontent.com/wallix/awless/master/getawless.sh | bash
You can also easily install completion for bash
or zsh
.
Check that awless
is properly installed by running:
> awless version
Securing your existing account
Now, to make your existing AWS account more secure using awless
, follow this steps:
- Enable MFA for your account
- Create/update the relevant policies/groups/roles (with MFA conditions if necessary) for your use case
- Move existing users and the current user into the appropriate groups
- Test that permissions are working as expected with MFA before removing permissions from the accounts without MFA
Conclusion
Implementing Multi-factor authentication of Amazon Web Services’s users for CLI/API access can be cumbersome. awless
eases the process with powerful scriptable commands while enforcing AWS best practices.
Have a look at the previous article on simplified user management for AWS with awless
and many more features on the awless repository!
Limitation to detach the MFA device
The AWS ManageOwnMFADevice
policy makes it impossible to securely allow a user assuming a role to use MFA to detach only its MFA device. This policy requires the user to use MFA to detach its MFA device (iam:DeactivateMFADevice
). This is good security as it prevents an attacker controlling the user’s credentials — but not the MFA device — to replace the user MFA device and escalade privileges. Through our article, as the user has assumed a role for MFA, AWS could not resolve the ${aws:username}
variable in the policy.
You can circumvent that by using the AWS console signed in as the user with MFA and deactivate the MFA device.
As an alternative, we could imagine in awless
using sts:GetSessionToken
to retrieve MFA temporary credentials for the user, instead of sts:AssumeRole
. Not implemented yet in the AWS SDKs, there is also no standard way to specify such a profile in the ~/.aws/config
configuration file (c.f. this opened issue).
Written by François-Xavier Aguessy, Henri Binsztok and Simon Caplette.