0
votes

I have an AWS Lambda function which has an execution role

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole",
                "logs:CreateLogGroup"
            ],
            "Resource": [
                "arn:aws:logs:eu-west-1:<acc-no>:*",
                "arn:aws:iam::<acc-no>:role/myreportingrole"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:eu-west-1:<acc-no>:log-group:/aws/lambda/<func-name>:*"
        }
    ]
}

The execution role has permission to use STS AssumeRole to a role (myreportingrole) within the same AWS account that has trust relationships with roles in other AWS accounts.

I want to to assume myreportingrole and then assume a role in another account.

I have some Python code which successfully assumes myreportingrole and allows me to query services in the current account. However, when I try to assume a role from another account, I get a accessdenied error

"errorMessage": "An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::<acc-no>:assumed-role/<exec-role>/<func-name> is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::<diff-acc-no>:role/roleX",

This is my Python code

import json
import boto3

def assume_local_role():
    sts_connection = boto3.client('sts')
    acct_b = sts_connection.assume_role(
        RoleArn="arn:aws:iam::<acc-no>:role/myreportingrole",
        RoleSessionName="lambdaTesting"
    )
    ACCESS_KEY = acct_b['Credentials']['AccessKeyId']
    SECRET_KEY = acct_b['Credentials']['SecretAccessKey']
    SESSION_TOKEN = acct_b['Credentials']['SessionToken']
    
    client = boto3.client(
        'ec2',
        aws_access_key_id=ACCESS_KEY,
        aws_secret_access_key=SECRET_KEY,
        aws_session_token=SESSION_TOKEN
    )
    return client

def assume_role():
    sts_connection = boto3.client('sts')
    acct_b = sts_connection.assume_role(
        RoleArn="arn:aws:iam::<diff-acc-no>:role/roleX",
        RoleSessionName="lambdaTesting"
    )
    ACCESS_KEY = acct_b['Credentials']['AccessKeyId']
    SECRET_KEY = acct_b['Credentials']['SecretAccessKey']
    SESSION_TOKEN = acct_b['Credentials']['SessionToken']
    
    client = boto3.client(
        'ec2',
        aws_access_key_id=ACCESS_KEY,
        aws_secret_access_key=SECRET_KEY,
        aws_session_token=SESSION_TOKEN
    )
    return client

def lambda_handler(event, context):
    # TODO implement
    client = assume_local_role() # Assumes myreportingrole
    client2 = assume_role() # Assumes roleX
    response = client2.describe_instances()
    print(response)
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

The error shows that the execution role is being used to assume RoleX, which does not have permission.

I can't figure out how to use myreportingrole in client2. Normally on EC2, I would use ~/.aws/config and credentials, however, being Lambda, I'm not sure if this is possible.

My understanding is that

`executionRole` assumes `myreportingrole`

credentials for `myreportingrole` then assumes `roleX`

However, what I'm getting is

`executionRole` assumes `myreportingrole`

`executionRole` assumes `roleX` and fails.

Is there a way to pass the credentials from client to client2?

2

2 Answers

2
votes

Your problem is that you call the same client factory in both assume_local_role() and assume_role(). To use the local role to assume the remote role, you need to use the credentials from the first to create the client for the second. Exactly like you're doing when creating the EC2 client.

It would look something like this (I'm just typing this in, there may be syntax errors, and I've put a blank line between each function call to make the flow clear):

def create_client_with_remote_role(local_role_name, remote_role_name, client_type):

    local_assume_client = boto3.client('sts')

    local_assume_result = local_assume_client.assume_role(
        RoleArn=local_role_name,
        RoleSessionName="lambdaTesting")

    remote_assume_client = boto3.client('sts',
        aws_access_key_id=local_assume_result['Credentials']['AccessKeyId']
        aws_secret_access_key=local_assume_result['Credentials']['SecretAccessKey']
        aws_session_token=local_assume_result['Credentials']['SessionToken'])

    remote_assume_result = remote_assume_client.assume_role(
        RoleArn=remote_role_name,
        RoleSessionName="lambdaTesting")

    return boto3.client(desired_client_type,
        aws_access_key_id=remote_assume_result['Credentials']['AccessKeyId']
        aws_secret_access_key=remote_assume_result['Credentials']['SecretAccessKey']
        aws_session_token=remote_assume_result['Credentials']['SessionToken'])
0
votes

You need to add a "trust" statement in the IAM Role definition used for the cross account role. You should also add a condition with an External ID, just to be safe. The below is similar:

AssumeRolePolicyDocument:
  Version: "2012-10-17"
  Statement:
    Effect: Allow
    Principal:
      AWS: <source-aws-account-id>
    Condition:
      StringEquals:
        "sts:ExternalId": <some-unique-identifier>
    Action:
      - "sts:AssumeRole"