Command line tool to enable accessing AWS using federated single sign on
Project description
mozilla-aws-cli
Command line tool to enable accessing AWS using federated single sign on
Prerequisites
- An OIDC provider like Auth0
- A well-known
openid-configuration
URL - An Auth0 application created
- Type : Native
- Allowed Callback URLs : A list of the localhost URLs created from the POSSIBLE_PORTS list of ports
- The
client_id
for this application will be used in the CLI config file
- An AWS Identity provider
- with an audience value of the Auth0 application client_id
- with a valid thumbprint
Instructions
Users can either configure Mozilla AWS CLI with a python package provided by their organization, or they can create a config file by hand.
Create a config file
The default files that configuration is fetched from are
- Windows
C:\Users\<user>\AppData\Roaming\Mozilla AWS CLI\config.ini
C:\ProgramData\Mozilla AWS CLI\config.ini
- Mac
/Users/<user>/.config/maws/config.ini
/etc/maws/config.ini
- Linux
/etc/xdg/xdg-ubuntu/maws/config.ini
(for Ubuntu)/home/<user>/.config/maws/config.ini
where settings in /etc
or C:\ProgramData
are overridden by settings in
C:\Users\<user>\AppData\Roaming\
or ~/.config/maws/
or /Users/
.
Users can also assert which config file(s) to read from using the -c
or --config
command line arguments.
These config files use the standard INI file format.
The config
file should contain a single section called [maws]
and can
contain the following settings.
There are three required settings which must either be set in a python package provided by the organization or in the user's config file. Those required settings are
well_known_url
: The OpenID Connect Discovery Endpoint URL. (Auth0)client_id
: The Auth0client_id
generated when the Auth0 application was created in the prerequisitesidtoken_for_roles_url
: The URL of the ID Token For Roles API. This URL comes from the location that the user's organization has deployed the idtoken_for_roles API. This API lets a user exchange an ID token for a list of groups and roles that they have rights to. This URL should be the base URL of the API, ending in/
Additional optional settings that can be configured in the config file are
-
scope
: A space delimited list of OpenID Connect Scopes. For exampleopenid
. Avoid including a scope which passes too much data which will exceed the maximum AWS allowed size of the ID Token (for example at Mozilla we neglect to include the raw full group list which is included in the ID Token when thehttps://sso.mozilla.com/claim/groups
scope is requested. -
output
: The output format for the tool to use. This must be one of the following valuesawscli
:maws
calls into theaws
application, configuring it directlyboto
: Outputs JSON to stdout to be directly consumed by the boto3 libraryenvvar
: A set of environment variables that load the temporary credentials directly in to the environment without writing them to a filejs
: Outputs JSON to stdout to be directly consumed by the AWS JavaScript SDKshared
: A set of environment variables that reference a dedicated maws AWS config file which is created
-
print_role_arn
: Whether or notmaws
should display the AWS IAM Role ARN on the command line. This can values likeyes
,no
,true
,false
The resulting config would looks something like this
[maws]
client_id = abcdefg
idtoken_for_roles_url = https://roles-and-aliases.example/roles
well_known_url = http://auth.example.com/.well-known/openid-configuration
Run the tool
There are various ways you can run maws
. The tool can output environment
variable setting text to activate your AWS session inside your terminal. Here
are some methods to use the tool.
Subcommand
You could run maws
within a $()
sub-shell and execute the results
- Interactively prompt for which IAM role to assume
$(maws)
- Pass the IAM role to assume as a command line argument
$(maws --role-arn arn:aws:iam::123456789012:role/example-role)
- Not only enable command line access to AWS, also log into the web console
$(maws -w)
:warning: Users of YADR and zsh: Subcommands can result in a broken authentication flow, and so it is recommended that you use either process substitution or
eval
, as described below.
Process substitution
This uses process substitution. Here are some examples of how you could run it
source <(maws -w)
Eval
You could eval the results
eval $(maws --role-arn arn:aws:iam::123456789012:role/example-role)
Copy paste
Take the output of the command and copy paste it into your terminal
maws
Using programmatically
In general, it is recommended to keep your code independent of maws
by
using environmental variables such as AWS_PROFILE
and letting the
underlying libraries read from your local AWS configuration. However,
there are situations (such as needing to connect to multiple accounts)
where this may not be an option.
In these cases, maws
can be used directly with libraries such as
boto3. To do this, capture the output
from maws
and use it directly in your program:
import boto3
import json
from subprocess import Popen, PIPE
if __name__ == "__main__":
with Popen(["maws", "-o", "boto"], stdout=PIPE) as proc:
boto_args = json.loads(proc.stdout.read())
s3_client = boto3.client('s3', **boto_args)
print(s3_client.list_buckets())
const AWS = require("aws-sdk");
const child_process = require("child_process");
const botoArgs = JSON.parse(child_process.spawnSync("maws", ["-o", "js"]).stdout);
new AWS.S3(botoArgs).listBuckets({}, (err, data) => {
console.log(data);
});
Sequence diagram
Notes
# https://community.auth0.com/t/custom-claims-without-namespace/10999
# https://community.auth0.com/t/how-to-set-audience-for-aws-iam-identity-provider-configuration/12951
Details
This is a collection of technical details that we've decided or discovered in building the mozilla-aws-cli
- The user group list should be set in the OIDC claim as a list of groups
instead of a string with delimiters
- The
amr
claim allows for passing a list - By using a list we don't need to worry about choosing a delimiter and ensuring the delimiter is not allowed in the group name
- The
ForAnyValue:StringLike
IAM policy condition operator doesn't need*
wildcard characters in the value since each group listed in the policy is a full group name which will match a full group name in the list passed inamr
- The
- Even if you only wish to allow a single user group to assume a role, you still
must use the
ForAnyValue:StringLike
operator, not theStringLike
operator. It's not clear why this is the case. - AWS has a maximum size that either the
id_token
or theamr
assertion can be.- When this maximum size is exceeded AWS returns the error
PackedPolicyTooLarge Serialized token too large for session
- It's possible that the size limit is not able to be determined because
AWS performs a packing or compression step on it's size such that the size
of the
amr
assertion doesn't have a linear relationship with the size of the object AWS tests against it's limit - For example a
amr
value that is a list of 30 group names with anid_token
length of 800 characters triggers this error.
- When this maximum size is exceeded AWS returns the error
- Currently, when a user logs into Auth0 for the first time and performs a Duo
MFA authentication, Auth0 overwrites the
amr
assertion that we create with a new list containing a single element["mfa"]
. We've opened a bug with Auth0 in hopes that they will change to appending to theamr
assertion.- If they make this change, the
amr
assertion would, in that case, contain the list of groups and what would appear like a group calledmfa
. We would need to do some checks to ensure that nobody starts using a real group calledmfa
- If they make this change, the
- The
amr
assertion in the OIDC spec isn't supposed to be used to pass a list of groups. It's also not supposed to be used to pass a string likeauthenticated
like AWS does with cognito.- The purpose of the
amr
assertion is to provide an RP with a list ofidentifiers for authentication methods used in the authentication
- RFC8176 states
The "amr" values defined by this specification are not intended to be an exhaustive set covering all use cases. Additional values can and will be added to the registry by other specifications.
- The RFC then goes on to define a list of allowed values
which make it clear that
authenticated
or group names are not correct - Given this, it's possible that down the road
- AWS will begin to use a different assertion than
amr
to conform to the spec - Auth0 will disallow setting non conforming values in
amr
- AWS will begin to use a different assertion than
- If this happens we would need to change how we do things
- The purpose of the
- By having an Auth0 rule that queries some external resource (such as the group to role mapping file) and added delay to login is introduced and a risk of a problem in fetching the mapping file which could cause login to fail
- We use the
amr
assertion because it appears to be the only way to pass data to AWS- The documentation
indicates that there are 3 assertions that can be used in IAM policy
conditions,
aud
oaud
andsub
- In testing we've found that
aud
is passed and we use it for the Auth0 client IDsub
is passed and we use it for the Auth0 usernameoaud
is not passedamr
is passed
- The documentation
indicates that there are 3 assertions that can be used in IAM policy
conditions,
- By passing a group list in the
amr
assertion we take on the following risks- At some point some user may try to login to AWS with SSO and login will fail
due to the
PackedPolicyTooLarge
error. This will occur when- Enough AWS account holders across our many AWs accounts create IAM policies which allow a diverse set of user groups to access various roles
- This unlucky user has access to so many different AWS accounts and roles
because they work across many teams that the union of all the AWS groups
which grant them access to the various roles exceeds the
PackedPolicyTooLarge
limit
- We can't be sure at the point that we send the assertion that it will fail
because we can't know the hard limit on the size of the
amr
assertion or theid_token
in total
- At some point some user may try to login to AWS with SSO and login will fail
due to the
- We plan to try to log and track users experience over time to see if the
group list size issue is becoming a problem. To do so we'll want to see
- The size of the
amr
assertion being passed each time a user logs in - If AWS ever returns a
PackedPolicyTooLarge
error
- The size of the
Supported IAM Policy Features
The Auth0 rule which finds the intersection in the groups a user is a member of with the union of all groups used in all AWS accounts IAM policies won't support all IAM policy operators. Here are the various use cases and whether they are supported or not
Supported
An AWS account holder wants to
- enable users that are members of group "foo" to assume role
arn:aws:iam::123456789012:role/baz
- supported
StringLike
,StringEquals
- enable users that are members of group "foo" as well as users that are members
of group "bar" to assume role arn:aws:iam::123456789012:role/baz
- supported
StringLike
,StringEquals
for a list of values
- enable users that are members of any group like "fo*" to assume role
arn:aws:iam::123456789012:role/baz
- supported
StringLike
with wildcards
Not Supported
An AWS account holder wants to
- enable users that are members of both group "foo" and group "bar" to assume
role arn:aws:iam::123456789012:role/baz
- not supported
- multiple
StringLike
orStringEquals
conditions
- enable users that are members of group "foo" but not allow users that are
members of group "FOO" to assume role arn:aws:iam::123456789012:role/baz
- not supported
- when assembling the group list to pass to AWS, we will do case insensitive matching. Additionally, there shouldn't ever be a case where two groups exist with the same characters in their name but different cases
- multiple
StringEquals
conditions where the values differ only in case
- enable users that are not members of group "bar" to assume role
arn:aws:iam::123456789012:role/baz
- not supported
StringNotEquals
,StringNotLike
- enable users that are members of group "foo" but not members of group "bar"
to assume role arn:aws:iam::123456789012:role/baz
- not supported
- multiple conditions including
StringNotEquals
,StringNotLike
Troubleshooting
If you don't see a role listed in the role picker which you would expect to have access to, possible reasons are :
- The IAM role was recently modified and
- the hourly scanner hasn't yet run to update the list of available roles.
- the list of available roles is current but the API that sits in front of it is using an out of date cached copy
- the list of available roles is current but the Auth0 rule is using an out of date cached copy of the available roles and as a result, isn't passing an "amr" claim with your current complete list of groups
- If the cause is 1 or 2 you can still assume that role, just not using this menu. Instead pass the role ARN on the command line.
- The conditions in the role don't allow you to access it because
- The role has a different "Principal" "Federated" value than it should
- Dev
- Federated :
arn:aws:iam::*:oidc-provider/auth.mozilla.auth0.com/
- Aud :
N7lULzWtfVUDGymwDs0yDEq6ZcwmFazj
- Federated :
- Prod
- Federated :
arn:aws:iam::*:oidc-provider/auth-dev.mozilla.auth0.com/
- Aud :
xRFzU2bj7Lrbo3875aXwyxIArdkq1AOT
- Federated :
- Dev
- The role has the wrong "Action" value which should be
sts:AssumeRoleWithWebIdentity
- The role has an "aud" condition that doesn't match the Auth0 client ID
being passed in the "aud" claim from Auth0
- Dev :
xRFzU2bj7Lrbo3875aXwyxIArdkq1AOT
- Prod :
N7lULzWtfVUDGymwDs0yDEq6ZcwmFazj
- Dev :
- The key name of the "aud" condition is incorrect
- Dev :
auth-dev.mozilla.auth0.com/:aud
- Prod :
auth.mozilla.auth0.com/:aud
- Dev :
- The key name of the "amr" condition is incorrect
- Dev :
auth-dev.mozilla.auth0.com/:amr
- Prod :
auth.mozilla.auth0.com/:amr
- Dev :
- You aren't a member of any of the groups listed in "amr" conditions
- The role has a different "Principal" "Federated" value than it should
- Your AWS account does not delegate security auditing rights to the Enterprise Information Security team so the group role map builder can't scan the IAM roles in your AWS account
- There is a bug
- in the Auth0 rule that filters the list of groups that you are a member of such that the "amr" claim returned to you is missing a group that you need to meet an IAM Role condition
- in the group role map builder that produces the map of groups to roles to enable the Auth0 rule and the role picker menu to know which roles are available to you
- in the ID token for role API that allows you to exchange your ID token for a list of roles so that the role picker can show you a menu of available roles
Development
When developing the tool and testing you can run it without installing it like this
python -m mozilla_aws_cli.cli --role-arn arn:aws:iam::123456789012:role/example-role
Note : You must run python -m mozilla_aws_cli.cli
instead of
python mozilla_aws_cli/cli.py
because mozilla_aws_cli uses absolute imports.
Creating enterprise / organization configuration
If you want to deploy the Mozilla AWS CLI across your organization and establish
default configuration values without requiring users to create config files you
can do so by implementing a standard mozilla_aws_cli_config
module.
Here are the steps assuming an example organization called Yoyodyne
- Create a new code repo. A good name would be
mozilla-aws-cli-yoyodyne
- In that repo create a
setup.py
#!/usr/bin/env python from setuptools import setup setup( name="mozilla-aws-cli-yoyodyne", description="Yoyodyne specific deployment of the mozilla_aws_cli", install_requires=["mozilla_aws_cli"], packages=["mozilla_aws_cli_config"], url="https://github.com/yoyodyne/mozilla-aws-cli-yoyodyne", version="1.0.0", )
install_requires
depends on themozilla_aws_cli
to ensure that if you instruct the user topip install mozilla-aws-cli-yoyodyne
they will get the Yoyodyne config and the tool
- Create a directory called
mozilla_aws_cli_config
- This is the reserved / well known module name that every organization can
implement. This name must be
mozilla_aws_cli_config
exactly and not include any part of your organization name (e.g. Yoyodyne)
- This is the reserved / well known module name that every organization can
implement. This name must be
- Within that
mozilla_aws_cli_config
directory create a single__init__.py
file. This will contain your organizations default configuration settings - In this
__init__.py
file create a single variable calledconfig
containing your organizations default configuration settings.- Yoyodyne's
__init__.py
might look likeconfig = { "client_id": "abcdefghiJKLMNOPQRSTUVWXYZ012345", "idtoken_for_roles_url": "https://roles-and-aliases.sso.yoyodyne.com/roles", "well_known_url": "https://auth.yoyodyne.auth0.com/.well-known/openid-configuration" }
- Yoyodyne's
The resulting repository called mozilla-aws-cli-yoyodyne
would look like this
mozilla-aws-cli-yoyodyne/
├── mozilla_aws_cli_config
│ └── __init__.py
└── setup.py
Other projects in this space
- https://github.com/aidan-/aws-cli-federator
- https://github.com/Nike-Inc/gimme-aws-creds
- https://github.com/sportradar/aws-azure-login
- https://github.com/oktadeveloper/okta-aws-cli-assume-role
- https://github.com/jmhale/okta-awscli
- https://github.com/prolane/samltoawsstskeys
- https://github.com/physera/onelogin-aws-cli
- https://github.com/kxseven/axe/blob/master/bin/subcommands/axe-token-krb5formauth-create
- https://github.com/openstandia/aws-cli-oidc
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file mozilla_aws_cli-1.2.1.tar.gz
.
File metadata
- Download URL: mozilla_aws_cli-1.2.1.tar.gz
- Upload date:
- Size: 136.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/46.4.0 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.7.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f7826ec4b9ed02078e4223fa0cbfeb56ca328fcb033360720cdc71a9b22657cf |
|
MD5 | a7379f5a7105244e2b366f948a4b3a9e |
|
BLAKE2b-256 | fae50859e4de468c7e05ddafdc6bd3ec5f3f8c2f137a95c7db497a3ac4b0b833 |
File details
Details for the file mozilla_aws_cli-1.2.1-py2.py3-none-any.whl
.
File metadata
- Download URL: mozilla_aws_cli-1.2.1-py2.py3-none-any.whl
- Upload date:
- Size: 361.8 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/46.4.0 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.7.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c56c4b6619417fa529e7920c55defaafe4a9b9b8c587ca549533f6c62802a9b8 |
|
MD5 | a53679de430d1aa1033283e490279eea |
|
BLAKE2b-256 | e81ce416104f4e3336f648064843b186b595be1eebf6ff40fbef35483f851247 |