awscli_saml_sso is a command line tool that aims to get temporary credentials from SAML identity
Project description
awscli_saml_sso is a command line tool that aims to get temporary credentials from SAML identity provider in order to authenticate to awscli.
Requirements
Python 3.8 to 3.11
Microsoft Edge web browser installed on operating system
Installation
You need a fully functional python 3 environment, then you can install tool from pypi:
pip install awscli-saml-sso
Usage
You only need to run the following command in terminal:
awscli_saml_sso
> ...
# Please choose the role you would like to assume:
# [ 0 ]: arn:aws:iam::<account_number>:role/<role_name>
# [ 1 ]: arn:aws:iam::<account_number>:role/<role_name>
# ...
# Selection: <select among numbered roles>
# ----------------------------------------------------------------
# Your new access key pair has been stored in the AWS configuration file /home/.aws/credentials under the saml profile.
# Note that it will expire at 2020-12-01 13:17:27+00:00.
# After this time, you may safely rerun this script to refresh your access key pair.
# To use this credential, call the AWS CLI with the --profile option (e.g. aws --profile saml ec2 describe-instances).
# ----------------------------------------------------------------
# Simple API example listing all S3 buckets:
# ['your-lovely-bucket', ...]
ask you to fill in required identity provider url in the form of https://<fqdn>:<port>/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices
opens a headless web browser to fulfil SSO authentication through your identity provider, asking you to input username, password and MFA
retrieve attached AWS roles and ask you to choose role you would like to assume
provide a saml profile in /home/.aws/credentials filled with temporary credentials
You can see what is happening in the web browser by appending --show-browser (headless mode is not used)
You can also use the browser to input username and passord by appending --use-browser (headless mode is not used)
After a successfull run with identiy provider nickname MyTenant, you can run this to avoid any prompt other than MFA
awscli_saml_sso --use-stored --idp-nickname=MyTenant
At the end, you just need to use AWS cofigured saml profile to authenticate your awscli calls
aws --profile saml ec2 describe-instances
OR
AWS_PROFILE=saml aws ec2 describe-instances
Features
Authenticate through SAML identity provider in web browser
Select among retrieved AWS roles you are allowed to assume
Store temporary credentials in aws configuration files
How it works
This section aims to explain how awscli-saml-sso works internally. When you authenticate through awscli-saml-sso, you will follow this workflow:
First a web browser is opened at the given identity provider start url
You will authenticate with your credentials (and MFA if required)
If authentication succeed, you will be redirected to AWS SAML REDIRECT URL which leads to several cases: * If you belong to multiple roles, a web page let you choose which one you would like to assume * If you belong to only one role, you should be automatically redirected to AWS console authenticated through the given role * If you do not belong to any role, an error page is returned to you
Whatever the case, your browser should close automatically and awscli-saml-sso will report the SAML authentication result to you. * Given the case, you should need to choose a role to assume * or the authenticate workflow stop here if you do not belong to any role
Finally awscli-saml-sso has automatically provided a saml profile in your aws credentials file which is authenticated through AWS STS temporary credentials which should by default expire in one hour.
What is the awscli-saml-sso secret sauce to make the work transparently for you?
At first, we choose to not make any assumption on the way your identity provider let you authenticate (how is named username/password fields, would you need to answer a challenge, required MFA step, …). Instead we choose to open a web browser which will let you follow your regular SSO authentication workflow. This web browser is driven by selenium, awscli-saml-sso will try to detect which browser is installed on your system and required web driver is automatically downloaded for you.
When authentication workflow ended, you will be redirected to AWS SAML REDIRECT URL. Here, thanks to a proxy configured in the previously opened web browser, we are able to detect that you reach redirect url, thus we can close web browser from now on.
In the redirect HTTP request, we find a SAMLResponse attribute in body that is base64 encoded, which correspond to SAML response in XML format. You can find an example here.
The most interesting part for us is the saml:AttributeStatement block enclosed here, which should contains those attributes:
RoleSessionName: should correspond to your authenticated username
Role: list of AWS roles you belong to that you are authorized to assume
SessionDuration: optional attribute that can override default one hour session duration from identity provider side
<saml:AttributeStatement>
<saml:Attribute FriendlyName="Session Duration"
Name="https://aws.amazon.com/SAML/Attributes/SessionDuration"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">28800
</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute FriendlyName="Session Name" Name="https://aws.amazon.com/SAML/Attributes/RoleSessionName"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">admin
</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute FriendlyName="Session Role" Name="https://aws.amazon.com/SAML/Attributes/Role"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
arn:aws:iam::000000000000:role/Role.User,arn:aws:iam::000000000000:saml-provider/SamlExampleProvider
</saml:AttributeValue>
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
arn:aws:iam::000000000000:role/Role.Admin,arn:aws:iam::000000000000:saml-provider/SamlExampleProvider
</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
In our case, we will parse SAML Role Attribute to print to user the list of AWS roles it is allowed to assume. Each role is in the form of <aws_role_arn>,<aws_identity_provider_arn>, for instance: arn:aws:iam::000000000000:role/Role.User,arn:aws:iam::000000000000:saml-provider/SamlExampleProvider.
Finally we call aws assume_role_with_saml through boto3 python sdk which expect the following arguments:
role_arn: the aws_role_arn retrieved previously
principal_arn: the aws_identity_provider_arn retrieved previously
saml_assertion: the base64 encoded saml response retrieved previously
AWS STS response will be retrieved and stored in a saml profile configured this way:
config.set("saml", "aws_access_key_id", sts_response["Credentials"]["AccessKeyId"])
config.set("saml", "aws_secret_access_key", sts_response["Credentials"]["SecretAccessKey"])
config.set("saml", "aws_session_token", sts_response["Credentials"]["SessionToken"])
config.set("saml", "aws_security_token", sts_response["Credentials"]["SessionToken"])
Note that you can call assume-role-with-saml directly from awscli this way:
awslocal sts assume-role-with-saml \
--role-arn arn:aws:iam::000000000000:role/Role.Admin \
--principal-arn arn:aws:iam::000000000000:saml-provider/SamlExampleProvider \
--saml-assertion $(cat docs/examples/keycloak_saml_response.xml | base64)
… which should give you response like:
{
"Credentials": {
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"SessionToken": "FQoGZXIvYXdzEBYaDwL8pPz/cNvhUKkibZTashetWcPahlTMbaBUvDwXxjiehDkRQGYYUQrTrMdv7+6SinGiDNBiB7ZKEoyfDja6vhHwnBP2UcY/XozN+MFFPGEMhHcsUqPApwOErN37uHAM5kIOukhGlNmIPvPVWZtDoWryAuygKbqZTWwKecCwtURG2I0KF8MpS+s6SaG6EOUl5OJf/mJJQvH725q2VOWUk7HBezFCIXO+t3L8SzMygdt2FNzwUenhazYvDs2ngSlsbFbAaeeMHikZrWgTs6GkUv1uyAknpTRnInmwBDHb7SZAqpDmc7Q9+b+NXTcO1qzx/eMarHHlFQyeEEI3BEc=",
"Expiration": "2020-12-06T18:54:38.114Z"
},
"AssumedRoleUser": {
"AssumedRoleId": "AROA3X42LBCD9KGW7O43L:benjamin.brabant",
"Arn": "arn:aws:sts::123456789012:assumed-role/Role.Admin/benjamin.brabant"
},
"Subject": "AROA3X42LBCD9KGW7O43L:benjamin.brabant",
"SubjectType": "persistent",
"Issuer": "http://localhost:3000/",
"Audience": "https://signin.aws.amazon.com/saml",
"NameQualifier": "B64EncodedStringOfHashOfIssuerAccountIdAndUserId="
}
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Please make sure to update tests as appropriate. For further information, please read CONTRIBUTING document.
Development
If you would like to setup awscli-saml-sso for local development, please read the following section. Before beginning, ensure to comply with requirements defined in Requirements section.
You should create a python virtual environment:
virtualenv -p python3 .venv
# OR
python3 -m venv .venv
# THEN
source .venv/bin/activate
You can figure out useful development requirements in requirements_dev.txt and install them:
pip install -r requirements_dev.txt
Then install a local editable version of awscli-saml-sso project with pip. Under the hood, the following command will create an awscli-saml-sso.egg-link file in .venv/lib/python3.8/site-packages/ directory which contains a path pointing to your current awscli-saml-sso project directory.
# from awscli-saml-sso project root
pip install -e .
Thus you will be able to use development version of awscli_saml_sso cli. Please check that this command correctly link to your local virtual environment:
which awscli_saml_sso
> /path/to/your/project/directory/.venv/bin/awscli_saml_sso
To ensure that awscli_saml_sso work properly, you will need:
A configured SAML identity provider
An access to AWS account
To prevent having to manually setup these requirements, you will find a ready to use local setup configured through docker-compose.yml. This configuration will setup the following environment:
An instance of localstack which aims to replicate AWS services locally
A configured keycloak server, which will act as your identity provider
A postgresql instance as a database backend required for keycloak server
To setup this environment, just execute the following command:
docker-compose up -d
After waiting few minutes, complete environment should be up and running. You can run awscli-saml-sso this way to target localstack services endpoint instead of AWS default ones:
awscli_saml_sso --endpoint-url=http://localhost:4566 --use-browser --show-browser
# OR
ASS_ENDPOINT_URL=http://localhost:4566 awscli_saml_sso --use-browser --show-browser
Then create a new IDP by enetering +, providing a name such as LocalStack and url http://localhost:8080/auth/realms/master/protocol/saml/clients/amazon-aws
Once the Keycloack page is displayed on Edge browser, you can use one of these credentials :
Username |
Password |
Behavior |
---|---|---|
aws_user |
aws_user |
You should get credentials for Role.User |
aws_admin |
aws_admin |
You should be able to choose between two AWS roles : Role.User and Role.Admin |
aws_void |
aws_void |
You should get no credentials because the account is not associated to any role |
Localstack
The provided localstack instance setup a local server on port 4566 that can be used as an AWS backend for required services. You can override the local exposed port by defining LOCALSTACK_EXPOSED_PORT environment variable.
You can interact with localstack this way, for instance to list existing buckets:
AWS_ACCESS_KEY_ID='_not_needed_locally_' AWS_SECRET_ACCESS_KEY='_not_needed_locally_' aws --endpoint-url=http://localhost:4566 s3 ls
To ease local usage, you can leverage awslocal cli which is configured properly to rely on localstack backend:
awslocal s3 ls
On container startup, localstack will automatically execute localstack-setup.sh script which will provision default resources:
An AWS S3 bucket named example-bucket
An AWS SAML provider named SamlExampleProvider
AWS roles named Role.User and Role.Admin which would be assumed by SSO users after authentication
Keycloak
The provided keycloak instance setup a local server on port 8080 that can be used as an identity provider backend. You can override the local exposed port by defining KEYCLOAK_EXPOSED_PORT environment variable.
Keycloak expose a web interface that can be accessed at http://localhost:8080.
You can authenticate to keycloak administration console with following credentials:
username: admin
password: admin
On container startup, keycloak will automatically import master-realm-with-users.json configuration which will provision default resources:
An urn:amazon:webservices client aims to register AWS as a SAML service provider
Role mapping has been properly defined with default provided users and groups.
Following users has been defined:
AWS ADMIN * username: aws_admin * password: aws_admin * groups: AWS_ADMINS, AWS_USERS
AWS USER * username: aws_user * password: aws_user * groups: AWS_USERS
AWS VOID * username: aws_void * password: aws_void * groups: N/A (not attached to any group)
Thus you can now use the following url as your identity provider url when asked by awscli-saml-sso: http://localhost:8080/auth/realms/master/protocol/saml/clients/amazon-aws
Please feel free to update keycloak configuration from administration console to fulfil your needs. If you think that your configuration should be setup by default, you can export it this way, replace master-realm-with-users.json content then submit your pull request :)
docker-compose run --rm -v $(pwd)/export:/tmp/export keycloak -Djboss.socket.binding.port-offset=100 -Dkeycloak.migration.action=export -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.file=/tmp/export/master-realm-with-users.json
> [...]
> 13:21:15,119 INFO [org.keycloak.services] (ServerService Thread Pool -- 67) KC-SERVICES0033: Full model export requested
> 13:21:15,925 INFO [org.keycloak.services] (ServerService Thread Pool -- 67) KC-SERVICES0035: Export finished successfully
> 13:21:15,119 INFO [org.keycloak.exportimport.singlefile.SingleFileExportProvider] (ServerService Thread Pool -- 67) Exporting model into file /tmp/export/master-realm-with-users.json
> [...]
When you read above logs, you can hit CTRL+C to stop running instance. You will find a master-realm-with-users.json file in export directory created in your current path.
Credits
AWS - How to Implement Federated API and CLI Access Using SAML 2.0 and AD FS AWS SAML based User Federation using Keycloak
License
awscli_saml_sso is open source software released under the GNU GPLv3.
Project details
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 awscli_saml_sso-0.3.0.tar.gz
.
File metadata
- Download URL: awscli_saml_sso-0.3.0.tar.gz
- Upload date:
- Size: 879.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.11.2 requests/2.32.3 setuptools/65.5.0 requests-toolbelt/1.0.0 tqdm/4.66.5 CPython/3.11.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 88044ebce87acef1db45056a2f3d59c47a3af8bb4c50f0faef397354591a9b89 |
|
MD5 | 789f84c509e07475657e99d2dedf902f |
|
BLAKE2b-256 | c953843075ab87388593696b293cb7edca0ed28d5251bfc4bafc5a3a5438f5ee |
File details
Details for the file awscli_saml_sso-0.3.0-py2.py3-none-any.whl
.
File metadata
- Download URL: awscli_saml_sso-0.3.0-py2.py3-none-any.whl
- Upload date:
- Size: 871.4 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.11.2 requests/2.32.3 setuptools/65.5.0 requests-toolbelt/1.0.0 tqdm/4.66.5 CPython/3.11.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 3087a3b8b17bd6c1ef6ceab191f50ec50d1969cdc6499b5b8d23ad396dda46a1 |
|
MD5 | 036e193002733f8f0b0bd07b3b358bfd |
|
BLAKE2b-256 | 2023987508618c29e0a4fcf33f4e48152ecff615ddecd52c5613d35da8f46c40 |