简体   繁体   中英

Boto3 AWS MFA authentication fails when run on Mac Native and PyCharm terminals, but works on VSCode terminal

I have written a script to query a DynamoDB table using python SDK and Boto3. The AWS IAM role requires MFA, and using VSCode terminal (Version: 1.71.1 - Universal) I can successfully authenticate and run the following to get session_token for creating an authenticated Dynamo DB client:

def create_authenticated_dynamo_dict() -> dict:
    user_serial_number = input('Enter ARN serial number, e.g. arn:aws:iam::123456789012:mfa/user: ')
    user_role = 'arn:aws:iam::XXXXXXXXXXXXXXX:role/{role_name}'
    mfa = input('Enter the user device MFA code: ')
    client = boto3.client('sts')
    mfa_sts_client = client.get_session_token(
        DurationSeconds=900,
        SerialNumber=user_serial_number,
        TokenCode=mfa
    ) ...

However, when I run this same code in PyCharm (PyCharm 2022.2.2) or in the native Mac terminal (M1 2020, macOS Monterey v 12.5.1), I am getting an extra (additional) request for MFA from AWS (after inputting the MFA code to terminal input), and the authentication fails with this error:

botocore.exceptions.ClientError: An error occurred (AccessDenied) when
calling the GetSessionToken operation: Cannot call GetSessionToken
with session credentials

I verified zsh shell for VSCode, PyCharm, and macOS terminal - but clearly there's some config difference between PyCharm/Mac native terminals and VSCode. I would like to run this in PyCharm. Ultimately I want to understand why this is occurring, and specifically what is causing an additional request for MFA token, when it should be read from the arguments to client.get_session_token .

I was eventually able to authenticate successfully on Pycharm, VSCode, and native OSX terminal. I believe the problem was that in both Pycharm or native OSX, Boto3 will default to assuming the most recently exported AWS profile in the current terminal window. This explains the unexpected extra request for MFA - the user values from my script were simply being ignored and the session credentials being used were incorrect.

So I solved this by always running the script from a new terminal window. I also updated Pycharm (2022.2.3). Here is the full method I am now using to authenticate:

  1. Create new boto3 sts client.
  2. get_session_token on this client, with user MFA code.
  3. Create another new boto3 sts client, using credentials from the first client.
  4. assume_role with the second client, and user IAM role.
  5. Create new boto3 DynamoDB client, using credentials from second client.

With this client, I was able to successfully make a DynamoDB query using the name of the table, as shown below:

user_serial_number = input('Enter ARN serial number, e.g. arn:aws:iam::123456789012:mfa/user: ')
role = input('Enter AWS Role: e.g. arn:aws:iam::XXXXXXXXXXXXXXXX:role/{name of aws role from credentials file}: ')
mfa = input('Enter the MFA code: ')

mfa_sts_client = boto3.client('sts')

# Credentials are good for 1 hour
mfa_credentials = mfa_sts_client.get_session_token(
    DurationSeconds=3600,
    SerialNumber=user_serial_number,
    TokenCode=mfa
)['Credentials']

# Create a new client with credentials from the MFA enabled session we created above:
assumed_role_sts_client = boto3.client(
    'sts',
    aws_access_key_id=mfa_credentials['AccessKeyId'],
    aws_secret_access_key=mfa_credentials['SecretAccessKey'],
    aws_session_token=mfa_credentials['SessionToken']
)

# assume role with the authenticated client
assumed_role_res = assumed_role_sts_client.assume_role(
    RoleArn=role,
    RoleSessionName='{session name can be anything}'
)

# get temporary credentials from the assumed role
tmp_creds = assumed_role_res['Credentials']

# create authenticated DynamoDB client with the temporary credentials
dynamo_client = boto3.client(
    'dynamodb',
    region_name='{aws region of table, e.g. 'us-east-1'}',
    aws_access_key_id=tmp_creds['AccessKeyId'],
    aws_secret_access_key=tmp_creds['SecretAccessKey'],
    aws_session_token=tmp_creds['SessionToken']
)

response = dynamo_client.describe_table(
    TableName='{name_of_table}'
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM