Enhanced account switching for AWS, supports Yubikey as MFA source
awsu
)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
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:
awsu
by using the register
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 Yubikeyawsu
and - after a double-dash —
- you specify the program you want to run in a given profile (e.g. admin
); depending on the profile awsu
will
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:
aws
as e.g. /usr/local/bin/aws
awsu
by using the environment variable style of parameter passingawsu register :iam-username
will perform the following actions:
:iam-username
: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:
:iam-username
:iam-username
awsu console
will open (in a browser) the link to the AWS console for a profile. It supports:
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
profile
ormfa-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:
awsu
supports Yubikey USB smartcards hereWhen 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:
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
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:
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)This summary leads to the following basic setup recommendations:
awsu
handles this problem by re-assuming roles that are due to expire in a configurable amount of timeWith 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:
/master/
iam:PermissionsBoundary
condition to all applicate IAM actions
Please don't hesitate to contact us when you need consulting around the design of your organizational setup.