What's a good way to obtain STS credentials with a SAML federated login via Okta for use in local command line tools?
amazon-web-services, boto, okta, python, saml
Solution
This project on GitHub titled: okta-aws-cli-assume-role is by far the easiest way to accomplish this that I've found.
Setup Script
I've created this shell script to pull it down and set things up:
$ cat okta_setup.sh
#!/bin/bash
print_usage() {
echo "Usage: $(basename $0) -a <aws_account> [-h] -r <aws_role> -u <octa_username>" >&2
}
print_help() {
cat <<EOF
Setup Script for Octa AWS CLI tool
==================================
To Execute:
$(basename $0) -a <aws_account> -r <aws_role> -u <octa_username>
Parameters:
-a: AWS Account to have setup with octa
-h: Option to get help
-r: The role to be assumed when logging into AWS
-u: The Octa username to be used to log in
EOF
}
while getopts ":a:hr:u:" opt; do
case ${opt} in
a )
awsAcct=$OPTARG;;
h )
print_help
exit 0
;;
r ) awsRole=$OPTARG;;
u ) octaUser=$OPTARG;;
\? )
print_usage
exit 1
;;
esac
done
if [ -z "${awsAcct}" ]; then
echo "AWS Account is required, use the -a option" >&2
exit 2
fi
if [ -z "${awsRole}" ]; then
echo "AWS Role is required, use the -r option" >&2
exit 3
fi
if [ -z "${octaUser}" ]; then
echo "Octa user is required, use the -u option" >&2
exit 4
fi
########################################
### Setup AWS CLI
########################################
echo 'Setting up ~/.aws/config'
echo '------------------------'
echo ''
aws configure set credential_process "okta-credential_process arn:aws:iam::${awsAcct}:role/${awsRole}" \
--profile "${awsRole}_${awsAcct}"
aws configure set region us-east-1 --profile "${awsRole}_${awsAcct}"
echo ''
echo "Here's the contents..."
echo '----------------------'
cat ~/.aws/config
echo ''
echo ''
########################################
### Setup Okta CLI
########################################
echo 'Setting up Okta CLI'
echo '-------------------'
echo ''
PREFIX=~/.okta bash <(curl -fsSL https://raw.githubusercontent.com/slmingol/okta-aws-cli-assume-role/master/bin/install.sh) -i
echo ''
########################################
### Create My config.properties for Okta
########################################
echo 'Setting up Okta CLI config.properties'
echo '-------------------------------------'
echo ''
cat << EOF > ~/.okta/config.properties
#OktaAWSCLI
OKTA_ORG=myurl.okta.com
OKTA_AWS_APP_URL=https://myurl.okta.com/...
OKTA_USERNAME=${octaUser}
OKTA_BROWSER_AUTH=false
OKTA_ENV_MODE=true
#OKTA_MFA_CHOICE=GOOGLE.token:software:totp
OKTA_STS_DURATION=43200
OKTA_AWS_REGION=us-east-1
OKTA_AWS_ROLE_TO_ASSUME=arn:aws:iam::${awsAcct}:role/${awsRole}
OKTA_PASSWORD_CMD=lpass show --password mydom.com
#OKTA_PASSWORD_CMD=echo "mypassword"
EOF
echo ''
echo "Here's the contents...."
echo '-----------------------'
cat ~/.okta/config.properties
echo ''
echo ''
Example
When you run this script:
$ ./okta_setup.sh -a 1234567890 -r MySystemsAdminAccess -u smingolelli
Setting up ~/.aws/config
------------------------
Here's the contents...
----------------------
[profile MySystemsAdminAccess_1234567890]
credential_process = okta-credential_process arn:aws:iam::1234567890:role/MySystemsAdminAccess
region = us-east-1
Setting up Okta CLI
-------------------
Installing into ~/.okta
Latest release JAR file: https://github.com/oktadeveloper/okta-aws-cli-assume-role/releases/download/v2.0.4/okta-aws-cli-2.0.4.jar
Fetching JAR file → ~/.okta/okta-aws-cli-2.0.4.jar
Symlinking ~/.okta/okta-aws-cli.jar → okta-aws-cli-2.0.4.jar
Creating example ~/.okta/config.properties
Add the following to ~/.bash_profile or ~/.profile:
#OktaAWSCLI
if [[ -f "$HOME/.okta/bash_functions" ]]; then
. "$HOME/.okta/bash_functions"
fi
if [[ -d "$HOME/.okta/bin" && ":$PATH:" != *":$HOME/.okta/bin:"* ]]; then
PATH="$HOME/.okta/bin:$PATH"
fi
Setting up Okta CLI config.properties
-------------------------------------
Here's the contents....
-----------------------
#OktaAWSCLI
OKTA_ORG=myurl.okta.com
OKTA_AWS_APP_URL=https://myurl.okta.com/......
OKTA_USERNAME=smingolelli
OKTA_BROWSER_AUTH=false
OKTA_ENV_MODE=true
#OKTA_MFA_CHOICE=GOOGLE.token:software:totp
OKTA_STS_DURATION=43200
OKTA_AWS_REGION=us-east-1
OKTA_AWS_ROLE_TO_ASSUME=arn:aws:iam::1234567890:role/MySystemsAdminAccess
OKTA_PASSWORD_CMD=lpass show --password mydom.com
#OKTA_PASSWORD_CMD=echo "mypassword"
What happened?
The above does several things:
- Adds a profile configured to use okta-cli's jar as a `credential_process`
- Installs and configures okta-cli in directory `$HOME/.okta`
- Configures a functioning `config.properties` file
You'll want to add this directory to your `$HOME/.bash_profile` or `$HOME/.bashrc` so that shows up on your `$PATH`.
if [[ -d "$HOME/.okta/bin" && ":$PATH:" != *":$HOME/.okta/bin:"* ]]; then
PATH="$HOME/.okta/bin:$PATH"
fi
Getting your STS token
With that you can now run AWS CLI commands or Boto3 Python scripts and they'll either interactively prompt you to log into Okta or do so and retrieve an STS token for you.
$ withokta "env" | grep TOK
AWS_SESSION_TOKEN=FQoGZX................5W4sIwW66bYJn9AEh6XeXO2aGKKrcy+sF
-or-
$ aws --profile MySystemsAdminAccess_1234567890 sts get-caller-identity
{
"UserId": "XXXXXXXXXXXXXXXX:smingolelli",
"Account": "1234567890",
"Arn": "arn:aws:sts::1234567890:assumed-role/MySystemsAdminAccess/smingolelli"
}
Keep in mind that the `withokta "<cmd>"` can include any command which you'd want to have the traditional environment variables available:
- AWS_SECRET_ACCESS_KEY
- AWS_ACCESS_KEY_ID
- AWS_SESSION_TOKEN
The method above where I'm calling the `aws` CLI directly is making use of another helper script included with the Okta CLI, `okta-credential_process`. This is added into your `$HOME/.aws/config` file and refers to the AWS role you want to assume. You can run it interactively to better see what it returns to `aws` when called:
$ okta-credential_process arn:aws:iam::1234567890:role/MySystemsAdminAccess
{"Expiration":"2019-09-08T00:49:45.615579Z","Version":1,"SessionToken":"XXXXXXXX","AccessKeyId":"XXXXXXXXXX","SecretAccessKey":"XXXXXXXXXX"}
That payload is the 3 components:
- AWS_SECRET_ACCESS_KEY
- AWS_ACCESS_KEY_ID
- AWS_SESSION_TOKEN
Problem
The Amazon Web Services API provides the AssumeRoleWithSAML endpoint to allow a user to exchange a SAML assertion for a set of temporary API credentials from the AWS Security Token Service. A SAML provider, like Okta, will generate a SAML assertion after a user logs into their web UI and Okta authenticates the user on that user's enterprise backend (e.g. enterprise LDAP). Typically this assertion is then relayed from the users browser onto another web service that accepts SAML assertions, the relying party, in order to authenticate the user to this third party (for example when using Okta federated login to enable a user to log into the AWS web console) What is the best way to enabled a federated user to authenticate with Okta, get an assertion, pass that assertion to STS and get back a set of temporary AWS API credentials that the user could then use with either the AWS command line tools, or with a local python boto script? - Launch a web browser from a python tool using the Python webbrowser module? - What's a fluid way to get an assertion from a web browser into a form usable by a command line tool? - Create a temporary ngrok tunnel to a locally running temporary webserver (e.g. an instance of flask or bottle) for Okta to redirect the users web browser onto in order to deliver the assertion to some local code? - How does one typically bridge the world of an interactive web page and local command line tools?