awsu
provides a convenient integration of AWS virtual MFA devices into commandline based workflows. It does use Yubikeys to provide the underlying TOTP one-time passwords but does not rely on additional external infrastructure such as e.g. federation.
There is also a high-level video overview from This Is My Architecture Munich:
[ Installation | Usage | Configuration | Caching | Commands | General multifactor considerations ]
Production-ready Mac releases can be installed e.g.through brew
via kreuzwerker/homebrew-taps:
brew tap kreuzwerker/taps && brew install kreuzwerker/taps/awsu
Linux is only available for download from the release tab. No Windows builds are provided at the moment.
awsu
relies on shared credentials files (the same configuration files that other tools such as e.g. the AWS commandline utilities are also using) being configured. The profiles are used to determine
- which IAM long-term credentials (access key pairs) are going to be used
- if a / which virtual MFA device is going to be used
- if a / which IAM role is going be used
In contrast to the official AWS CLI awsu
also supports putting an mfa_serial
key into a profile which contains long-term credentials (instead of a role). In this case a virtual MFA device is always used when using the long-term credential profile in question.
An abstract overview of the usage workflow of awsu
looks like this:
- You ensure you fulfill the prerequisites above
- You start using
awsu
by using theregister
command: this will create a virtual MFA device on AWS, store the secret of that device on your Yubikey and enable the virtual MFA device by getting two valid one-time passwords from the Yubikey - Now you just run
awsu
and - after a double-dash—
- you specify the program you want to run in a given profile (e.g.admin
); depending on the profileawsu
will- determine the access-key pair to use
- optionally use TOTP tokens from your Yubikey device (to get a session token from AWS in order to add the MFA context to the request) and
- optionally assume an IAM role
- use the credentials resulting from these operation(s), cache them and export them to the environment of the program specified after the double-dash
Given the following shared credentials config:
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[foo]
aws_access_key_id = AKIAIFODNNOS7EXAMPLE
aws_secret_access_key = bPxRfiCYEXAMPLEKEY/K7MDENG/wJalrXUtnFEMI
mfa_serial = arn:aws:iam::123456789012:mfa/[email protected]
[bar]
mfa_serial = arn:aws:iam::123456789012:mfa/[email protected]
role_arn = arn:aws:iam::123456789012:role/foo-cross-account
source_profile = default
[wee]
external_id = 1a03197b-3cb5-491b-bc06-84795afc95ef
mfa_serial = arn:aws:iam::123456789012:mfa/[email protected]
role_arn = arn:aws:iam::121234567890:role/bar-cross-account
source_profile = default
[gee]
role_arn = arn:aws:iam::121234567890:role/wee-cross-account
source_profile = foo
awsu
will produce the following results:
Profile | Credentials | Cached? | MFA? |
---|---|---|---|
default |
Long-term* | No* | No* |
foo |
Short-term session-token | Yes | Yes, from "foo" itself** |
bar |
Short-term session-token, then role | Yes | Yes, from "bar" itself |
wee |
Short-term session-token, then role with external ID | Yes | Yes, from "wee" itself |
gee |
Short-term session-token, then role | Yes | Yes, from "foo" |
*) unless a MFA is specified as parameter to awsu
- then a short-term session-token (equivalent to foo
) is produced
**) the form of using mfa_serial
directly long-term credential profiles is not supported by the official AWS CLI (it will just ignore it) - please note that this form will always produce short-term credentials which may not be useful in some circumstances e.g. when re-registering a previously unregistered virtual MFA device
In the following the global configuration parameters are described. Note that all parameters are implemented as flags with environment variable equivalents - when these start with AWS_
(like the setting of profiles) they have equivalent semantics to e.g. other SDK using applications such as the AWS CLI.
These options describe the currently selected profile and the locations of the shared credential files.
Long | Short | Environment | Default | |
---|---|---|---|---|
Currently used profile | profile |
p |
AWS_PROFILE |
default |
Shared config file location | config-file |
c |
AWS_CONFIG_FILE |
Platform |
Shared credentials file | shared-credentials-file |
s |
AWS_SHARED_CREDENTIALS_FILE |
Platform |
These options describe caching options, session token and role durations and other aspects of short-term credentials.
Description | Long | Short | Environment | Default |
---|---|---|---|---|
Disable caching | no-cache |
n |
AWSU_NO_CACHE |
false |
Duration of session tokens & roles | duration |
d |
AWSU_DURATION |
1 hour, maximum depends on config of the role in question (up to 12 hours) |
Grace period until caches expire - in other words: the time a session will be guaranteed to be valid | grace |
r |
AWSU_GRACE |
45 minutes |
Source of OTP tokens | generator |
g |
AWSU_GENERATOR |
yubikey - can be set to manual if you want to manually enter OTP passwords |
MFA serial override | mfa |
m |
AWSU_SERIAL |
None - can be used to set or override MFA serials |
Description | Long | Short | Environment | Default |
---|---|---|---|---|
Verbose logging | verbose |
v |
AWSU_VERBOSE |
false |
Caching is only used for cacheable (short-term) credentials. It can be disabled completely (even on a case-by-case basis) and is controlled by two primary factors: duration and grace.
The duration is always equivalent to the duration of the session token used which - in turn - is equivalent to the duration of the optionally assumed role (1 hour by default).
The grace period is the minimum safe distance to the duration before it's considered invalid (45 minutes by default). This is useful for dealing with long-running deployments that might be interrupted when e.g. a role becomes invalid mid-deployment.
Example: in the default setting with a duration of 1 hour and a grace period of 45 minutes awsu
will consider cached credentials invalid after 15 minutes.
The default command (invoking just awsu
) has two modes: export and exec. It supports just the global parameters described above.
When awsu
is invoked without additional arguments, the resulting credentials are exposed as shell exports. In this mode, awsu
can be used with eval
to actually set these variables like this:
eval $(awsu)
After using export mode, credentials can used until they expire.
When awsu
is invoked with a doubledash (--
) as the last argument it will execute the application specified after the doubledash (including all arguments) with the resulting credentials being set into the environment of this application.
In exec mode it makes sense to use shell aliases to drive awsu
like e.g. in zsh:
alias aws="awsu -v -- aws"
alias terraform="awsu -v -- terraform"
# etc ...
Note that when using this alias style:
- you can always reference the alias targets with absolute paths to temporarily escape, e.g. by referring to
aws
as e.g./usr/local/bin/aws
- you can still configure
awsu
by using the environment variable style of parameter passing
awsu register :iam-username
will perform the following actions:
- create a new virtual MFA device that is named after the
:iam-username
- store the secret key of this device with a name derived from the virtual MFA's serial number (ARN) that is compatible with Yubikey
- enable the virtual MFA device with the given
:iam-username
After successful registration awsu
will log the serial of the MFA for usage as mfa_serial
in your profiles.
The following parameters are exclusive to register
.
Description | Long | Short | Environment | Default |
---|---|---|---|---|
Generates a QR code of the MFA secret that can be used for backup purposes on smartphones | qr |
q |
AWSU_QR |
true |
Sets the "issuer" part of the QR code URL - depending on the smartphone app used this may add stylistic information to the one-time passwords (e.g. an icon) | issuer |
i |
AWSU_ISSUER |
Amazon |
awsu unregister :iam-username
will perform the following actions:
- deactivate the virtual MFA device that is named after the
:iam-username
- remove the matching TOTP secret from the Yubikey
- delete the virtual MFA device that is named after the
:iam-username
awsu console
will open (in a browser) the link to the AWS console for a profile. It supports:
- long-term credentials
- short-term credentials for
- cross-account ("internal") roles
- external ("federated") roles (role with an
external_id
field in their profile)
The following parameters are exclusive to console
.
Description | Long | Short | Environment | Default |
---|---|---|---|---|
Opens the resulting link in a browser (as opposed to just returning it) | open |
o |
- |
true |
awsu token
will generate a fresh one-time password from an inserted Yubikey. In order to determine the correct secret it will
- use the MFA directly configured on the currently used
profile
or - use the the MFA configured through the
mfa-serial
parameter.
The goal of this section is to consider the multifactor options that are available on AWS without involving additional external infrastructure (e.g. by utilizing federation). Under these constraints there are two options available:
- Restricing access by IPv4 addresses and/or networks, expressed in CIDR notation
- Easy to implement in AWS and requires no additional effort except for the control of static IPv4 IPs or ranges
- Hard to restrict to the right people in an organization e.g. your operators network vs. your backoffice network or guest wifi network
- Some impact on remote workers - you'll need a VPN solution that routes all traffic for AWS IP addresses (or just all traffic)
- Difficult to spoof unless you are a very privileged attacker
- Restricing access by proving the possession of a virtual or hardware MFA device, specifically a device that can emit 6-digits TOTP one-time passwords (OTP)
- More difficult to implement in AWS since you'll want to introduce smartcards as sources for OTPs (unless users want to type in OTPs every n minutes) -
awsu
supports Yubikey USB smartcards here - Easy to restrict in usage to the right people since MFA devices are first-class entities in IAM
- No impact on remote workers
- Difficult to spoof due to the aggressive rate-limits of MFA authentication attempts - since AWS uses a 6-digit configuration it's nevertheless possible and failures to authenticate with MFA should be monitoried through AWS CloudTrail
- More difficult to implement in AWS since you'll want to introduce smartcards as sources for OTPs (unless users want to type in OTPs every n minutes) -
When possible we recommend to require both seconds factors.
IP based second factors are provided implicitly by virtue of being the address from which a request originates.
MFA based second factors are provided through one of the following mechanisms:
- Using long-term credentials (the access key pair associated with an IAM user) to get a session token via STS
- this yields new short-term credentials (a new access key pair plus the session token itself) that now contains
- the information about the MFA context but
- no new principal (the IAM user remains the principal)
- a session token can be requested to be valid for a period between 15 minutes and 12 hours - there is no way of directly configuring an upper ceiling
- this yields new short-term credentials (a new access key pair plus the session token itself) that now contains
- Using long- or short-term credentials to assume a role via STS
- this yields new short-term credentials with a new principal (the role)
- when using long-term credentials the MFA serial and OTP token must be passed to the STS API when assuming the role
- when using short-credentials aquired from the session token mechanism described above the MFA serial and OTP token can be omitted
- in both cases the role now includes a MFA context
- a role can be requested to be valid for a period between 15 minutes and 12 hours - it's upper ceiling (between 1 hour and 12 hours, with 1 hour being the default) is configured directly on the IAM role
- this yields new short-term credentials with a new principal (the role)
Second factor requirements are expressed in the form of global conditions keys in the the optional Condition
block that is found in most of the available types of IAM policies such as
- Trust policies of roles
- Policies attached to e.g. roles or users
- Permission boundaries attached to e.g. roles or users
- Selected service policies e.g. S3 bucket policies
Service control policies that can be applied globally on AWS Organization members are not supported at the moment.
Regardless of its location the Condition
for an IP-based second factor would look like this:
"IpAddress": {
"aws:SourceIp": "203.0.113.0/24"
}
The Condition
content for an MFA-based second factor can have two forms.
The "age" form:
"NumericLessThan": {
"aws:MultiFactorAuthAge": "3600"
}
Or the "boolean" form:
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
The "age" form is preferred as the means of requiring a MFA. Since a session token (see the section before) cannot directly be configured for a maximum duration the "age" form is the only way to set an acceptable window for the proof of possession of a MFA device.
The type of policy where a second factor requirement is enforced at has some implications.
When applied to a non-trust policy, the requirement is enforced on every interaction. That also means that if a permission is denied (e.g. due to aws:MultiFactorAuthAge
) the user has not always a way of knowing exactly why it's denied (in some AWS APIs the UnauthorizedOperation
response contains an EncodedMessage
that can be decoded using the appropriate STS API - this may or may not clarify a permission issue).
When applied to a trust policy, the requirement is only enforced when assuming the role. Since a trust policy usually only regulates the acceptable principals (e.g. an AWS account) and second factor conditions the user should be able to deduce why the permission to assume the role has been denied (e.g. no access to the admin role in this account, not in the office / not dialed into the VPN, no MFA device active). Both approaches can also be mixed either through policies or through permission boundaries (under the usability constraints described above).
Since an assumed role also has an explicit time-to-live that is clearly visible to users only using trust policies for MFA conditions makes MFA based second factors easier to handle in practice. This approach can be summarized as following:
- A role requires the proof of posession once at the beginning of it's lifetime through an
aws:MultiFactorAuthAge
condition that should set to the duration attribute (the maximum lifetime) of a role (as discussed above: since an older session token could have been used to assume the role, the "boolean" form is not sufficient) - The longer a role can last, the greater the window of opportunity gets for an attacker to gain control of the principal (e.g. by gaining access to the computer where the short-term credentials reside)
- Therefore long lasting session should have less permissions than shorter lasting roles
This summary leads to the following basic setup recommendations:
- Plan a permission schema that differentiates between the levels of access different functional roles should have - typical examples include the AWS Managed Policies for Job Functions as well as e.g. roles for removing MFA devices from IAM users in self-service
- Map those functional roles to IAM roles
- Role durations and matching MFA conditions should reflect the level of access the associated policies will have
- Consider that API operations carried with roles that expire mid-deployment will get aborted mid-deployment and that 1 hour might not be enough for certain operations to complete (such as e.g. RDS deployments) -
awsu
handles this problem by re-assuming roles that are due to expire in a configurable amount of time - Consider adopting an AWS Organization setup with multiple member accounts and locate these roles in different accounts
- Map the right to assume these IAM roles to IAM groups
- Also give users in these groups (by using policy variables) the right to create and enable a virtual MFA device for themselves
- Do not directly allow them to deactive and delete their own MFA device (this would allow an attacker to simply replace an MFA device with one they control) - monitor deletions via CloudTrail and consider outsourcing this to dedicated privileged users or - at minimum - require second factors for the MFA deletion alone (directly or via a role dedicated to this purpose)
- Do not give IAM users any other direct permissions
- Add IAM users to IAM groups appropriate for their function roles
With second factor conditions in various policies (likely in multiple accounts) in place one must consider protecting them from modification by regular and privileged users.
Privileged users in this context are users that can create and update IAM resources beyond the self-service permissions describe in the section above. Such privileged users are an unfortunate reality in AWS where service-linked permissions are not always fine-granular enough or are not supported at all. The unfortunate part is that it's currently impossible to restrict role creation to service principals which implies that privileged users can create cross-account roles to arbitrary accounts.
The following approaches are recommend in order to protect your second factor requirements:
- Implement all IAM that regulate access under a dedicated path, e.g.
/master/
- Create an IAM policy ("master") that restricts create, update and delete operations on IAM resources under this path
- attach this IAM policy as a permission boundary to your IAM roles
- enforce the attachment of this permission boundary by expressing it as a
iam:PermissionsBoundary
condition to all applicate IAM actions
- Carefully monitor the creation of roles closely and consider implementing indirections (e.g. using the AWS Service Catalog) as an alternative to directly creating IAM roles (e.g. for usage in instance profiles) and IAM users (e.g. for creating SMTP credentials for SES)
Please don't hesitate to contact us when you need consulting around the design of your organizational setup.