Cross Account IAM: Roles
This is the second half of 2 articles about cross-account IAM. In the last article we saw how to give access to other accounts using Resource Policies.
In this article, we'll see how to do the same using Roles and "Role Assumption".
Just like before, we'll pretend we some resources in one account - an ECR repository with container images.
Then we have another account that needs to use that ECR repository - maybe so our Kubernetes can pull and run a container image.
We need the second account to be able to pull ECR images from the first account.
Both Accounts Need to Agree
Both accounts need to agree that they can interact with the resources in question. One account has to grant access to the other account, and the other account has to decide it's allowed to use that granted access.
This isn't always obvious, especially if one side is using an AdministratorAccess
role (which effectively grants all access to everything using *
for all criteria).
In any case, there are two(ish) ways to accomplish cross-account IAM:
- Resource Policies
- Role assumption
We'll talk about them both. This article will discuss Role Assumption.
Role Based Policies and Assumption
Rather than use Resource Policies, we'll now use Identity-based Policies. This means we'll use pure IAM (e.g., the IAM dashboard if you're click-opsing this stuff) instead of adding Policies to a Resource directly.
Roles are another way we can get cross-account access to resources. We're gonna see how to do that here.
So, our setup: Once again, we have an ECR repository in account-a
, and we want account-b
to have access to it.
A role-based solution is this: account-a
creates a Role whose Policy allows permission to the ECR repository. This is all pretty normal stuff! If we create a role named account-a-role-name
, we can attach a Policy that looks something like this:
1{ 2 "Version": "2012-10-17", 3 "Statement": [ 4 { 5 "Sid": "allow-pull-on-this-repository", 6 "Effect": "Allow", 7 "Resource": [ 8 "<account-a-id>.dkr.ecr.<region>.amazonaws.com/<ecr-repo-name>" 9 ],10 "Action": [11 "ecr:BatchGetImage",12 "ecr:BatchCheckLayerAvailability",13 "ecr:GetDownloadUrlForLayer",14 "ecr:DescribeImages".15 "ecr:DescribeRepositories"16 ]17 },18 {19 "Sid": "QuarkyECRNeedsThisToBeASeparateStatement",20 "Effect": "Allow",21 "Resource": [22 "*"23 ],24 "Action": [25 "ecr:GetAuthorizationToken",26 ]27 }28 ]29}
So, this is the Role named account-a-role-name
. It's Policy allows permission to use the ECR repository.
Role Assumption + Trust Policy
Our goal is to get account-b
to be able to use this Role. The process is called Role Assumption. In this case, the way AWS using "assume" is :
To take on: To take upon oneself, undertake, or take over.
In other words, account-b
can be allowed to "assume" the Role from account-a
and obtain the permissions its attached Policies allow.
As always, both accounts have to agree on this happening! The first step is give the above Role in account-a
a "Trust Policy" that allows account-b
to assume it.
A Trust Policy is different from attaching an "identity-based" Policy to the Role. There's only one Trust Policy for any given Role, and every Role has one. It says who can use (assume) the Role.
So, to accomplish our goal, the Trust Policy for the role in account-a
will look something like this:
1{ 2 "Version": "2012-10-17", 3 "Statement": [ 4 { 5 "Sid": "allow-account-b-to-assume-this-role", 6 "Effect": "Allow", 7 "Principal": { 8 "AWS": [ 9 "arn:aws:iam::<account-b-id>:role/some-role-name"10 ]11 },12 "Action": "sts:AssumeRole"13 }14 ]15}
The principal (and conditions!) can be used similar to our setup with Resource Based Policies. The Principal can be an entire account, a specific Role, a specific IAM user, or more. Conditions can be used to further lock down who can assume this role and when.
What we did here is to say that the only Principal that can assume this Role is another Role in account-b
that is specifically named some-role-name
.
Account B's Requirements
Now, we've only done stuff in account-a
. We've created an ECR repository, and then created a Role with an attached Policy. That Policy gives permission to pull images from the ECR repository.
Then we added a Trust Policy that allows a specific Role in account-b
the ability to assume this Role.
We still need to do some thing inaccount-b
so it can use this ECR repository!
As noted, account-b
has to decide it can assume this Role. To do that, we need the Role referenced above to exist within account-b
- the one named some-role-name
. Create can create that role, and attach a regular "identity-based" permission Policy to that role that looks like this:
1{ 2 "Version": "2012-10-17", 3 "Statement": [ 4 { 5 "Sid": "AllowRoleAssumption", 6 "Effect": "Allow", 7 "Resource": [ 8 "arn:aws:iam::<account-a-id>:role/account-a-role-name" 9 ],10 "Action": "sts:AssumeRole"11 }12 ]13}
That Policy looks like a trust policy (it give sts:AssumeRole
permissions), but it's not - it's just a regular permission policy. This role in account-b
DOES have a trust policy (all roles do!) but it'll be super vanilla - just giving permission to users of account-b
to assume the Role. It will look like this:
1{ 2 "Version": "2012-10-17", 3 "Statement": [ 4 { 5 "Effect": "Allow", 6 "Action": "sts:AssumeRole", 7 "Principal": { 8 "AWS": "<account-b-id>" 9 },10 "Condition": {}11 }12 ]13}
In any case, note that we didn't give the the Role in account-b
permission to do anything to ECR. Instead, it's policy just gives permission to assume the role created in account-a
. At the same time, account-a
uses a Trust Policy that says this role in account-b
is allowed to assume it.
Once the Role from account-a
is assumed, by the Role in account-b
, the code doing the assuming will then have the ECR permissions of that assumed role!
How to Assume a Role
In a lot of cases, our applications, K8s utilities, AWS CLI's, or whatever can be told to assume a role and it'll just happen for us. Behind the scenes, someone wrote some code to assume a Role and it Just Works™ magically for us, and that's great.
However, you can see how Role Assumption works yourself! It is, quite literally, just another API call. You may have guessed that it's the sts:AssumeRole
API call.
If we're authenticated against account-b
and we are using some-role-name
, we can assume the Role from account-a
:
1# Pretend ~/.aws/credentials defines profile "account-b" and2# that profile uses "some-role-name"3# https://awscli.amazonaws.com/v2/documentation/api/latest/reference/sts/assume-role.html4aws --profile account-b sts assume-role \5 --role-arn="arn:aws:iam::<account-a-id>:role/account-a-role-name" \6 --role-session-name="some-arbitrary-session-name" \7 --duration-seconds=3600 # optionally set how long the session is valid for (3600 is default)
The output of such a call gives us credentials to use! These credentials are temporary, and let us act as the assumed Role:
1{ 2 "AssumedRoleUser": { 3 "AssumedRoleId": "<some-assumed-role-name>", 4 "Arn": "<some-arn>" 5 }, 6 "Credentials": { 7 "SecretAccessKey": "GivenRoleSecretKey", 8 "SessionToken": "GivenLongAsHellRoleSessionToken", 9 "Expiration": "2016-03-15T00:05:07Z",10 "AccessKeyId": "GivenRoleAccessKeyID"11 }12}
We care the most about the the following parts of the output:
- Access Key ID
- Secret Access Key
- Session Token
We can then use those temporary credentials! If we're in the CLI, that'll look like this:
1export AWS_ACCESS_KEY_ID=GivenRoleAccessKeyID2export AWS_SECRET_ACCESS_KEY=GivenRoleSecretKey3export AWS_SESSION_TOKEN=GivenLongAsHellRoleSessionToken4 5# Verify our assumed role6aws sts get-caller-identity7 8# Do some ECR things that our account-a role lets us do:9aws --region us-east-1 ecr describe-images --repository-name <ecr-repo-name>
The way I think about permissions is that it's best to use Role Assumption ("role chaining", if Roles are assuming other Roles). However, it's not always feasible to do role-chaining in this manner - for example, if we're using some libraries or package that haven't been programmed to assume a (secondary) role. In those cases, we can often fall back to Resource Policies.
AWS is complex. Sign up for free, useful lessons like this.