Account Root Principal in IAM Trust Policy Grants Every Identity, Not Just Root
A trust policy that names the account root ARN as Principal looks tightly scoped. It isn't: any IAM identity in the account with an sts:AssumeRole grant can use it.
Operational summary
At a glance
- Symptom
- An engineering team discovers, usually through a CloudTrail review or an access audit, that an IAM role intended for a single break-glass administrator can…
- Likely cause
- Specifying an AWS account, whether by account ID or root ARN, as a trust policy Principal grants an implicit assumption right to every IAM…
- Impact
- The blast radius is every permission attached to the assumed role, reachable by any current or future IAM identity in the account that acquires an sts:AssumeRole grant on…
- Verification signal
- Validation must show the role can still be assumed by every legitimate caller and can no longer be assumed by anyone else.Re-run aws iam get-role and confirm Principal…
- Safe correction
- Replace the account-root Principal with the specific IAM role or user ARNs that should be permitted to assume the role, and add a Condition that fails closed for…
- Rollback or recovery
- Rollback restores the previously captured trust policy document if the corrected policy blocks a legitimate workload.aws iam update-assume-role-policy --role-name ROLE_NAME --policy-document file://original-trust-policy.jsonStop condition: if any legitimate automated workload…
Symptom
An engineering team discovers, usually through a CloudTrail review or an access audit, that an IAM role intended for a single break-glass administrator can be assumed by dozens of unrelated IAM users and roles inside the same AWS account. The role’s trust policy looks deliberately narrow: it names only the account’s root ARN as the allowed Principal, and nobody on the team recalls granting anything broader. The role has never been touched since it was created, yet CloudTrail shows AssumeRole calls from service roles that have nothing to do with break-glass access.
False Assumption
The team assumed that naming the account root ARN (arn:aws:iam::ACCOUNT_ID:root) as the trust policy’s Principal restricts role assumption to the AWS account’s literal root user credentials, behaving like a single named identity. Under that assumption, only someone signed in as root, or explicitly delegated by root, could call sts:AssumeRole against the role.
Root Cause
Specifying an AWS account, whether by account ID or root ARN, as a trust policy Principal does not name the root user specifically. It delegates the assumption decision to the account’s own IAM policies. The trust policy by itself grants no identity access; the account’s identity-based policies decide which specific IAM users and roles are actually permitted to call sts:AssumeRole against that role’s ARN. Any IAM identity in the account whose identity-based policy allows sts:AssumeRole on the target role (for example, through Resource * or AdministratorAccess) can assume it, regardless of whether that identity policy existed before or after the trust policy was written, and without any further change to the trust policy itself.
Impact
The blast radius is every permission attached to the assumed role, reachable by any current or future IAM identity in the account that acquires an sts:AssumeRole grant on it, without triggering a further trust policy change or a security review. In practice this converts what looks like a tightly scoped, break-glass role into an account-wide privilege escalation path: an administrator who later attaches a broad sts:AssumeRole statement to an unrelated developer role, for a reason that has nothing to do with this trap, can let that developer role assume any role in the account whose trust policy names the account root, including this one, provided the statement’s Resource matches the target role (for example *).
Diagnosis
Confirm the pattern before changing anything. Retrieve the role’s current trust policy and check whether Principal references the account ARN or account ID rather than specific user or role ARNs.
aws iam get-role
--role-name ROLE_NAME
--query 'Role.AssumeRolePolicyDocument'
--output json
Expected evidence: a Statement.Principal.AWS value equal to arn:aws:iam::ACCOUNT_ID:root or the bare account ID, with no Condition block restricting the calling principal, for example no aws:PrincipalArn or sts:ExternalId condition. Cross-reference CloudTrail AssumeRole events for that role ARN over the widest available retention window to establish which principals have actually exercised the grant; this is what turns the finding from theoretical into material. Write down every legitimate caller found, including automated callers and service roles, because the corrected policy must list each of them.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "sts:AssumeRole"
}
]
}
Correction
Replace the account-root Principal with the specific IAM role or user ARNs that should be permitted to assume the role, and add a Condition such as an MFA requirement (callers must then assume the role with an MFA-authenticated session). Automated callers that cannot present MFA will be denied by that condition, so decide for each caller from the CloudTrail review whether it belongs in the list and whether it needs a separate statement. The intended break-glass principal must itself be in the Principal list and be able to satisfy MFA. Save the corrected document as corrected-trust-policy.json. Also audit identity-based policies that allow sts:AssumeRole broadly.
In the example below, the ARNs are placeholders for your actual intended callers. Each principal must exist when the policy is saved, otherwise the update is rejected. Do not name the role being edited as its own caller.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::123456789012:role/PLACEHOLDER_INTENDED_CALLER_1",
"arn:aws:iam::123456789012:user/PLACEHOLDER_INTENDED_CALLER_2"
]
},
"Action": "sts:AssumeRole",
"Condition": {
"Bool": { "aws:MultiFactorAuthPresent": "true" }
}
}
]
}
First capture the existing policy document so the correction is reversible, and check that the file is non-empty and contains the trust policy:
aws iam get-role --role-name ROLE_NAME
--query 'Role.AssumeRolePolicyDocument'
--output json > original-trust-policy.json
Do not run update-assume-role-policy until original-trust-policy.json exists and has been checked.
Before applying the change, confirm that every legitimate caller from the CloudTrail review appears in the new Principal list, and test the change first on a comparable role in a non-production account. Verify that the break-glass principal is in the list and can assume the role with MFA. Also confirm that someone with IAM permissions that do not depend on this role (for example another admin role or the root user) can run the rollback if needed. Then apply the corrected document:
aws iam update-assume-role-policy
--role-name ROLE_NAME
--policy-document file://corrected-trust-policy.json
Validation
Validation must show the role can still be assumed by every legitimate caller and can no longer be assumed by anyone else.
- Re-run
aws iam get-roleand confirm Principal now lists only the intended ARNs and the Condition block is present. - Ask each legitimate caller to run
aws sts assume-role --role-arn ROLE_ARN --role-session-name validation-testfrom their own credentials and confirm success. - Choose a test identity in the account whose identity-based policy allows
sts:AssumeRoleon the role, but which is not in the Principal list, and have it attempt the same call. Confirm it is denied with AccessDenied, which shows the trust policy is the control. - Monitor CloudTrail AssumeRole events for the role ARN for at least one full business cycle after the change to confirm no unexpected principal successfully calls it.
Rollback
Rollback restores the previously captured trust policy document if the corrected policy blocks a legitimate workload. It must be run by an identity with IAM permissions that does not rely on assuming this role.
aws iam update-assume-role-policy
--role-name ROLE_NAME
--policy-document file://original-trust-policy.json
Stop condition: if any legitimate automated workload fails to assume the role within the validation window, or an on-call escalation reports blocked access, apply the rollback command immediately and re-open diagnosis before reattempting the correction with an updated Principal or Condition list.
Prevention
Add an automated check, for example an IAM Access Analyzer custom check or a CI guard on Terraform or CloudFormation IAM role definitions, that flags any trust policy Principal equal to an account ID or root ARN without an accompanying Condition. Treat that pattern as a required manual security review gate rather than an accepted default. Record in the account’s IAM baseline which roles are intentionally account-wide, if any, versus which were assumed to be root-only by mistake, and review that list on the same cadence as other least-privilege reviews described in the AWS Well-Architected Security Pillar’s design principles.
Related reading
- AWS Cross-Account AssumeRole Is Denied by a Mismatched Trust Policy
- An AWS Principal Field Left as an Account ARN Silently Grants Every Role in That Account
- An IAM Permissions Boundary Fails to Block Decrypt Access Granted by a KMS Key Policy
- AWS Identity Controls Leave Workloads Exposed Until Verified
Apply the safer control
Before you change production
Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.
Fix commands and configuration
arn:aws:iam::ACCOUNT_ID:rootsts:AssumeRoleaws iam get-role
--role-name ROLE_NAME
--query 'Role.AssumeRolePolicyDocument'
--output jsonVerify, roll back or escalate
Verify
Validation must show the role can still be assumed by every legitimate caller and can no longer be assumed by anyone else.Re-run aws iam get-role and confirm Principal now lists only the intended ARNs and the Condition block is present.Ask…
Rollback
Rollback restores the previously captured trust policy document if the corrected policy blocks a legitimate workload.aws iam update-assume-role-policy --role-name ROLE_NAME --policy-document file://original-trust-policy.jsonStop condition: if any legitimate automated workload fails to assume the role within the validation window, or…
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.
Fixed? Get the next one before it hits you: The KBY Brief
Practical fixes for the systems you run, by email about twice a week. Unsubscribe at any time.
About this page
Dates
Published
Last updated
Corrections
- : Corrected during a review of published articles. Details
Was this useful?
Reports go to the site owner and are used to correct pages. Corrections are listed at /corrections.