aws-ct-execution-role-enter-account

A management-account principal that can assume AWSControlTowerExecution enters every enrolled member account as administrator.

derived aws emits CanEnterAccount

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanAssume {'ct_exec_role': None}

where

node_type(?ct_exec_role) == Role and ?ct_exec_role.name == 'AWSControlTowerExecution' account_of(?ct_exec_role) != account_of(?principal) # cross-account: member != mgmt account_of(?principal) == <organization management account> # SCPs do not apply here; the role only trusts the mgmt account ?principal holds EFFECTIVE sts:AssumeRole on the role ARN (identity policy — trust alone is insufficient)

emit

source typeIdentity
target typeAccount
source?principal
target<account of ?ct_exec_role>
permissionssts:AssumeRole
conditionstrust_relationship iam_permission
state logicACTIVE when ?principal is a management-account principal AND its identity policy grants sts:AssumeRole on arn:aws:iam::<member>:role/AWSControlTowerExecution (trust policy already admits the mgmt account). CONDITIONAL(iam_permission) when the caller's sts:AssumeRole scope is unresolved/only-wildcard; POTENTIAL when the caller is not confirmed to be a mgmt-account principal. This is the AWS cross-account CanAssume->CanEnterAccount case already generalized by can-control.yaml enter-account-via-assume — this rule specializes it to the CT role and drives the fan-out to EVERY enrolled account (one edge per member carrying AWSControlTowerExecution).

Narrative

{principal.name} (management account) can assume AWSControlTowerExecution in account {account.name}, obtaining AdministratorAccess there - a foothold in every Control-Tower-enrolled account.

Raw rule rules/derived/aws/controltower.yaml

id: aws-ct-execution-role-enter-account
emits: CanEnterAccount
description: A management-account principal that can assume AWSControlTowerExecution enters every enrolled
  member account as administrator.
match:
- - principal: null
  - CanAssume
  - ct_exec_role: null
where:
- node_type(?ct_exec_role) == Role and ?ct_exec_role.name == 'AWSControlTowerExecution'
- 'account_of(?ct_exec_role) != account_of(?principal)   # cross-account: member != mgmt'
- 'account_of(?principal) == <organization management account>   # SCPs do not apply here; the role only
  trusts the mgmt account'
- "?principal holds EFFECTIVE sts:AssumeRole on the role ARN (identity policy \u2014 trust alone is insufficient)"
emit:
  source_type: Identity
  target_type: Account
  source: ?principal
  target: <account of ?ct_exec_role>
  permissions:
  - sts:AssumeRole
  conditions:
  - trust_relationship
  - iam_permission
  state_logic: "ACTIVE when ?principal is a management-account principal AND its identity policy grants\
    \ sts:AssumeRole on arn:aws:iam::<member>:role/AWSControlTowerExecution (trust policy already admits\
    \ the mgmt account). CONDITIONAL(iam_permission) when the caller's sts:AssumeRole scope is unresolved/only-wildcard;\
    \ POTENTIAL when the caller is not confirmed to be a mgmt-account principal. This is the AWS cross-account\
    \ CanAssume->CanEnterAccount case already generalized by can-control.yaml enter-account-via-assume\
    \ \u2014 this rule specializes it to the CT role and drives the fan-out to EVERY enrolled account\
    \ (one edge per member carrying AWSControlTowerExecution)."
  confidence: min(contributing_confidences) * 0.95
  derived_from:
  - <CanAssume edge_id (principal -> AWSControlTowerExecution)>
  false_positive_note: "The role's trust policy admits the mgmt account, but assuming it STILL requires\
    \ the caller's own identity policy to grant sts:AssumeRole on the role ARN \u2014 do not emit ACTIVE\
    \ from the trust edge alone. Only management-account principals qualify (SCPs cannot restrict them,\
    \ and members are not trusted). Emit one CanEnterAccount per enrolled account that has the role; skip\
    \ accounts where the role was removed/renamed. Cite can-control.yaml enter-account-via-assume \u2014\
    \ do not duplicate the generic assume->enter roll-up."
  narrative: "{principal.name} (management account) can assume AWSControlTowerExecution in account {account.name},\
    \ obtaining AdministratorAccess there \u2014 a foothold in every Control-Tower-enrolled account."
move · open · esc close