Skip to content
Latchkey

How to Design a Least-Privilege OIDC Role for CI

A keyless role is only as safe as its trust scope and permission policy, so pin both to exactly what the job needs.

Least privilege for OIDC has two halves: who can assume the role (trust policy, scoped by sub) and what the role can do (permission policy, scoped to specific actions and resources).

Principles

  • One role per pipeline or environment, not a single shared admin role.
  • Trust policy pins sub to the repository and, for production, an exact ref or environment.
  • Permission policy lists explicit actions and resource ARNs, not wildcards.
  • Keep session duration short so a leaked session expires quickly.

Scoped permission policy

Trust policy (JSON)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject"],
      "Resource": "arn:aws:s3:::my-app-artifacts/*"
    },
    {
      "Effect": "Allow",
      "Action": "ecr:GetAuthorizationToken",
      "Resource": "*"
    }
  ]
}

Gotchas

  • A broad sub like repo:org/repo:* plus admin permissions makes any branch admin; avoid it.
  • Prefer separate deploy and read-only roles over one role with both.
  • Review CloudTrail for the role-session-name to confirm only intended runs assume the role.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →