Are AWS IAM role trust relationships comparable to resource based policies?

tl;dr at the bottom if you're familiar with the behavior if IAM roles when it comes to trust relationships.

Setup I created to describe my question:

The four roles

arn:aws:iam::<account-id>:role/A
arn:aws:iam::<account-id>:role/B
arn:aws:iam::<account-id>:role/X
arn:aws:iam::<account-id>:role/Y

and a user arn:aws:iam::<account-id>:user/Test who is able to assume the roles A and B directly via trust relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<account-id>:user/Test"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

The user Test is just for me to switch into role A and B easily. In the following I use "having assumed role A or B" as a starting point which is easy to do via aws sts assume-role on your CLI.

role A configuration

inline policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "arn:aws:iam::<account-id>:role/X",
                "arn:aws:iam::<account-id>:role/Y"
            ]
        }
    ]
}

The trust relationship is like mentioned above and shouldn't be relevant.

role B configuration

It doesn't have any additional config except the irrelevant trust relationship above.

role X configuration

trust relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<account-id>:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

role Y configuration

trust relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<account-id>:role/B"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

To sum up the differences: A has access granted to X and Y as inline policy. B has nothing like that but B is explicitly granted in the trust relationship of Y. X only allows acces for the account, not a specific user.

The test with A

If you assume A calling aws sts get-caller-identity should return something like

{
    "UserId": "<role-id>:test-session-A",
    "Account": "<account-id>",
    "Arn": "arn:aws:sts::<account-id>:assumed-role/A/test-session-A"
}

If you now do aws sts assume-role --role-arn arn:aws:iam::<account-id>:role/X --role-session-name a-assume-x you do get a valid response with credentials. If you do the same for Y you get AccessDenied [...] User [...] is not authorized to perform: sts:AssumeRole [...].

That does make sense because Y has no trust relationship with A.

The test with B

If you assume B calling aws sts get-caller-identity should return something like

{
    "UserId": "<role-id>:test-session-B",
    "Account": "<account-id>",
    "Arn": "arn:aws:sts::<account-id>:assumed-role/B/test-session-B"
}

Now it's the other way round. If you try to assume X, you get Access Denied and assuming Y works.

Assuming Y works because of the explicit grant of role B. We also use that when we assume A or B with the user Test in the first place. What is confusing to me is that assuming X doesn't work. The behavior itself makes sense. You allow anything in the account to assume X but you need a policy to grant access to a specific user.

tl;dr - my question

If you explicitly grant one specific principal access to a role, the principle gets direct access - no additional policy needed. This behaves like resource-based policies.

However, if you add a whole account as trust relationship, this behaves like a permission boundary. You can get access, but you have to create a policy for a principal inside the boundary to actually be able to assume.

This confuses me. What are trust relationships? What is the rule behind that behavior?

Bonus question: Does this also apply to other Services which can be in trust relationship like Lambda or RDS?


Not exactly sure how to answer this since you basically already know what trust relationships are.
They specify how can assume a role, specifically they can

  • grants access to a specific entity: role, user, idp, ... and / or
  • grants access to a specific account to delegate access

In the first case that is all you need.

In the second case you additionally need an attached policy that allows you an assumeRole operation. That account-granting includes your own account. A role without a trust relationship would not be assumable by anyone, not in the same account and not cross-account. Not sure why it way decided to be that way I could imagine that the reason was basically "just because the role is inside your account not everybody in that account should be able to assume it".

This is identical to how e.g. KMS key policies work.


They do not fully match how resource-based policies work according to the policy evaluation docs but mostly fit if you consider them as cross-account resource-based policies at which point both sides need to grant permission to an action for it to actually be allowed.