Can't grant cross-account access to a ECS task's role

3.3k Views Asked by At

Background:

I am trying to grant access for an ECS task in Account B to extract data from a DynamoDB table in Account A.

In theory, this works fine by: (1) creating a role in Account A that Account B is allowed assume (with a paired External ID), and then (2) granting that role access to the needed DynamoDB tables.

Problem:

When a process running in ECS assumes the ECS role (Account B), it creates a unique instance of that role, which apparently cannot be the target of a principal statement in the account. If I try granting access to the underlying role, that apparently has no affect.

Can I force ECS to use the original role, which I can grant as principal, rather an a temporary set which apparently can't then assume other roles?

The only workaround I can think of is to create a new user with programmatic API credentials, handoff those creds to the ECS task, and then then have the ECS task overrides it's own role with the one's belonging to the AWS key-pair. That's definitely an antipattern though, as far as I can tell, and it opens up the risk of those credentials being compromised.

Is there any way to do this without resorting to a manually created user and manually passed AWS creds?

Additional info:

  • I can grant to this principal arn:aws:iam::AcctB****:role/myrole but the ECS task is using this one at runtime: arn:aws:sts::AcctB****:assumed-role/myrole/45716b8c-40c8-4ca7-b346-1ff4ee94eb53.
  • Error message is: An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::AcctB****:assumed-role/myrole/45716b8c-40c8-4ca7-b346-1ff4ee94eb53 is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::AcctA****:role/ExternalRole
2

There are 2 best solutions below

0
On

I faced the same situation and found a safe way to programmatically assume the role from an ECS task.

sts_client = boto3.client('sts')

identity = sts_client.get_caller_identity()

print(identity) # identity in account A

response = sts_client.assume_role(RoleArn=assume_role_arn, RoleSessionName=session_name)

session = boto3.Session(aws_access_key_id=response['Credentials']['AccessKeyId'],
                        aws_secret_access_key=response['Credentials']['SecretAccessKey'],
                        aws_session_token=response['Credentials']['SessionToken'])

The idea is to connect to account A, assume role from account B, init a session using temporary credentials and then init whatever client you need from that session.

external_client = session.client('sts')

identity = external_client.get_caller_identity()

print(identity) # identity in account B

This is more secure than creating a IAM user and share credentials between accounts because authentication is done internally.

Here you can find more information about how it works https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sts.html

0
On

Linking the following topic here: How to assume an AWS role from another AWS role?

TL;DR
Really make sure you don't have a typo, wrong name or external id in one of the policies.

ECS needs to assume your Task Role the same way you are trying to assume the role in Account B. This can't be changed. Even ECS tries to assume Account B role with assumed-role/... it works as expected when granted a principal with role/...