23

I am developing python software which deals with AWS SQS queues. It uses boto3, mostly boto3.session.Session.

I have seen here that we can pass an aws_session_token to the Session constructor.

When running my code outside of Amazon, I need to periodically refresh this aws_session_token since it is only valid for an hour. So I need to reinstantiate a boto3.Session on my own.

I am just wondering how things work inside AWS. Do I need to manually refresh my sessions by getting a new aws_session_token through the environment? Or is my session valid "for ever"/is it handled internally so I don't have to refresh my AWS sessions?

The documentation seems unclear to me.

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
Geoffrey R.
  • 1,907
  • 1
  • 19
  • 32
  • The session token you are referring to is generated dynamically using the `assume_role()` method. The token (and the access and secret keys) generated using this API is valid for a specific duration (minimum 900 seconds). The maximum duration of the validity of the token is 12 hours (provided it is configured in the role). What I generally do is I retrieve the credentials every time I need it and set the duration to 900 seconds. – krishna_mee2004 Jul 10 '18 at 17:53

1 Answers1

48

Short answer:

AWS generated tokens do not last forever, and the same goes for any boto3 session created with generated tokens. But you can set a lengthy TTL on your tokens (up to 36 hours) as long as your tokens weren't generated with the account root user. This gives you a lot of time to do what you need to do with your Python script.

AWS has several ways of handling temporary and permanent access to your account. Generally, you'll want to rely on temporary credentials, as they are safer to use and align more with best practices. Boto3 uses a prioritized list of where it scans for credentials described here

Long, rambling answer:

I write a lot of automation code for dozens of AWS accounts, so I've dealt with this stuff a lot.

This assumes you're developing in Linux. Windows is very similar but has some differences.

There are (at least) three methods to handle remote access to your AWS account:

  1. Maintain a profile in your ~/.aws/credentials file which contains your AWS IAM user access keys, and run your Python script using that profile.
  • All your Python script has to do is create a boto3.session.Session object with no parameters. When you don't provide tokens or a profile name for the session instantiation, boto3 automatically looks for credentials by scanning through the credentials priority list described in the link above.

  • I don't recommend this at all, but it works and give you an idea of how AWS profiles are used. This is permanent access using your IAM user's API keys, which never expire. While you can use these keys for any action that your IAM user has been granted permission, you shouldn't use them for anything other than assuming specialized roles to do all other work.

  1. Assume a role using the AWS CLI from the command line, load the tokens into environment variables and then run your Python script.

    • Advantages:
      • Easily automated.
      • Can set the token TTL easily.
      • The tokens can be loaded into environment variables and become instantly available to your Python scripts.
    • Disadvantages:
      • Only practical if your Python script is interacting with one AWS account.
      • If your Python script runs longer than the token TTL (unlikely, but not impossible), then your script will hit an AccessDenied error and stop.
  2. Run the Python script and have it handle role assumption and token juggling.

    • Advantages:
      • Allows you to juggle access to multiple accounts in one place.
      • Can set the token TTL easily.
      • If tokens expire, you can catch the AccessDenied exception, refresh the tokens, and keep going.
    • Disadvantages:
      • Involves maintaining the Python code which gets the access tokens and creates boto sessions with them. Granted, it's not that much code, but it's still code, which means maintenance and clutter.

I generally prefer method 2 and strongly discourage method 1. Method 3 is situational.

Method 1:
From the command line, set your AWS_PROFILE variable to your profile name and run the script. Everything done in the script with uses your AWS profile (IAM user access keys).

AWS_PROFILE=<YOUR_CREDENTIALS_PROFILE_NAME> python <PATH_TO_SCRIPT>

Method 2:
From the command line, use your AWS profile to assume a role in the account, and then store the generated tokens in environment variables. Now when you execute the script, it will use those tokens automatically:

credentials=`AWS_PROFILE=<YOUR_AWS_PROFILE_NAME> aws sts assume-role --role-arn <YOUR_AWS_ROLE_NAME> --role-session-name <SOME_SESSION_NAME> --query 'Credentials.{AKI:AccessKeyId,SAK:SecretAccessKey,ST:SessionToken}' --output text`

export AWS_ACCESS_KEY_ID=`echo ${credentials} | awk '{print $1}'`
export AWS_SECRET_ACCESS_KEY=`echo ${credentials} | awk '{print $2}'`
export AWS_SECURITY_TOKEN=`echo ${credentials} | awk '{print $3}'`
export AWS_DEFAULT_REGION=<AWS_REGION>

python <path_to_your_python_script>

Note: since your tokens are loaded into environment variables, AWS_PROFILE should NOT be set when you run your script. All AWS SDKs automatically look for credential tokens in those environment variables. You can read more about them here.

Method 3:
In your Python code, generate the access tokens and then create a session with those tokens.

import boto3

role_info = {
    'RoleArn': 'arn:aws:iam::<AWS_ACCOUNT_NUMBER>:role/<AWS_ROLE_NAME>',
    'RoleSessionName': '<SOME_SESSION_NAME>'
}

client = boto3.client('sts')
credentials = client.assume_role(**role_info)
    
session = boto3.session.Session(
    aws_access_key_id=credentials['Credentials']['AccessKeyId'],
    aws_secret_access_key=credentials['Credentials']['SecretAccessKey'],
    aws_session_token=credentials['Credentials']['SessionToken']
)

Run your script the same as Method 1, except this time your AWS_PROFILE is used to assume the role and any subsequent work is performed through the role since the session is created with the assumed role.

AWS_PROFILE=<YOUR_CREDENTIALS_PROFILE_NAME> python <PATH_TO_SCRIPT>

Hope this helps!

Min Latt
  • 5
  • 1
  • 4
Himal
  • 3,002
  • 1
  • 18
  • 17
  • 2
    Awesome answer! Thanks a lot Himal. I'll try to rely on the 2nd method then. – Geoffrey R. Jul 11 '18 at 09:56
  • Thank you for this. I didn't realize at first you create the client, THEN a session based on the results of that client. I'm using get_session_tokens() and creating a session based on that response to validate MFA and this helped a lot. – glitchwizard Feb 22 '21 at 00:22
  • @Himal, How to do this without Assume Arn Role? – user190245 May 22 '21 at 17:34
  • https://stackoverflow.com/questions/67643392/python-boto3-mfa-making-connection-with-access-key-id-access-key-session-token?noredirect=1#comment119566149_67643392 , i tried without assume arn role in boto and it worked but how to do that in boto3 – user190245 May 22 '21 at 17:37