AWS Role Assumption
Understanding AWS Role Assumption and Trust Policies
Understanding AWS Role Assumption
In AWS, role assumption lets one role temporarily 'impersonate' another by requesting short-lived credentials via AWS STS. It’s a secure way to delegate access without passing around secret keys.
In AWS, a Trust Policy on the target role defines which identities are allowed to assume it.
Example AWS to AWS Trust Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789876:role/SomeDataTransferService"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "1234e567-89ab-cdef-0123-456789abcdef"
}
}
}
]
}
Example GCP to AWS Trust Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["sts:AssumeRoleWithWebIdentity"],
"Principal": {
"Federated": "accounts.google.com"
},
"Condition": {
"StringEquals": {
"accounts.google.com:sub": "123456789012345678901",
"accounts.google.com:oaud": "1234e567-89ab-cdef-0123-456789abcdef",
}
}
}
]
}
Interpreting the Trust Policy
The Trust Policy that is attached to the target role will include 2 conditions.
Condition 1: Scoping the permission to the Intended Principal
- For AWS to AWS this looks like:
/...
"Principal": {
"AWS": "arn:aws:iam::123456789876:role/SomeDataTransferService"
}
/...
- For GCP to AWS this looks like:
/...
"Principal": {
"Federated": "accounts.google.com"
},
"Condition": {
"StringEquals": {
"accounts.google.com:sub": "123456789012345678901"
}
/...
This condition ensures that only the designated assuming role can act as the target role. This is regardless of the ExternalId. For example, in the case of connecting to an S3 bucket, this condition tells AWS "there's a specific service account that is allowed to assume my AWS role in order to write data into this bucket".
Condition 2: Preventing the Confused Deputy problem (ExternalId)
- For AWS to AWS this looks like:
"StringEquals": {
"sts:ExternalId": "1234e567-89ab-cdef-0123-456789abcdef"
}
- For GCP to AWS this looks like:
"StringEquals": {
"accounts.google.com:oaud": "1234e567-89ab-cdef-0123-456789abcdef",
}
This condition ensures that a bad actor (in this case, a different account holder) cannot establish a connection by supplying another account holder's S3 bucket & role ARN (that previously granted permission to their own account), and coerce the "data transfer service" to act on that account.
When the transfer-service attempts to authenticate into the legitimate AWS/S3 role, the transfer-service will pass the Recipient ID of the bad actor, and AWS will block the authentication request, saying "The External ID I'm acting upon provided is BadActor123 because it attempting to act on behalf of Recipient BadActor123, but the Trust Policy on the S3 account is configured only for External ID LegitimateActor456".
What is the ExternalId?
The transfer service maintains an ExternalId (Recipient ID) for every account. These Recipient ID's are guaranteed to be unique, immutable, and are not configurable.
Updated about 6 hours ago