Identities, roles, and least privilege
A role's trust policy says who can become it; its permission policies say what it can do. Build permissions from the calls a job makes.
Two questions, two policies
Every role answers two separate questions. Who can become this role? That is the trust policy. For a CI deploy role it usually names the CI's identity provider and narrows it with a condition, for example only workflows from main of one repository. What can the role do once assumed? That is the set of permission policies, inline or attached. Mixing them up is common. A role with a tight trust policy and "Action": "*" is still an administrator for anyone who gets a workflow merged.
How a request is decided
A statement has an Effect (Allow or Deny), the Actions it covers (s3:PutObject, ecr:*), the Resources it applies to as ARNs, and sometimes a Condition. AWS starts from deny. A request is allowed only if some applicable statement allows it and none denies it. An explicit Deny always wins. Wildcards match more than they seem to: s3:* includes DeleteBucket, and a Resource of * includes every bucket in the account, including the ones holding customer data.
Least privilege from evidence
Write a role's policy from the job, not from what makes the error go away. List every API call the job makes and the resource each one touches. A deploy pipeline might log in to the registry, push one repository, upload under one bucket prefix, and update one service. Each line becomes a statement. A few actions, such as ecr:GetAuthorizationToken, have no resource to scope to and need "Resource": "*". Keep those statements to the single action. Then check both directions: the job's calls succeed, and the things it never does, such as touching another team's service, deleting repositories, or changing IAM, are refused. Policy simulators and access logs help, but the list in the pipeline file is the best evidence you have.
Temporary broad permissions have a way of becoming permanent. Give every exception an owner and an end date, and review roles that CI or automation can assume first. They are the ones an attacker reaches through your build system.
Key terms
- Principal
- The identity making a request, such as a user, a role session, or a service.
- Trust policy
- The resource policy on a role that says which principals may assume it.
- Permission policy
- A policy attached to an identity that lists what it may do on which resources.
- Explicit deny
- A Deny statement that matches a request. It overrides any Allow.
Read further
- AWS Identity and Access Management User Guide, Roles section, "Roles terms and concepts" (trust policy versus permissions policy) (Free online)
Who may assume a role, and how temporary credentials are issued, for example to a CI system through OIDC. - AWS Identity and Access Management User Guide, Reference, "Policy evaluation logic" (Free online)
Default deny, explicit allow, explicit deny, and how identity and resource policies combine within one account. - AWS Identity and Access Management User Guide, "Security best practices in IAM" (Free online)
Least privilege, refining permissions from access activity, and why wildcards in production roles are a risk.