Hub-and-spoke OIDC across AWS accounts
Every pipeline reaches every AWS account through one trusted entry point, so pipelines hold no long-lived keys, and a new repository or account deploys with no new trust to set up.
By: Benjamin Clark
Intent
This pattern assumes many repositories whose pipelines deploy into several accounts of an AWS Organization, through a CI/CD platform that can issue OpenID Connect (OIDC) tokens, such as GitHub Actions or GitLab CI/CD. It gives every pipeline one way in to AWS. A hub role, an AWS identity that issues temporary credentials to whoever is allowed to assume it, is the only thing in the organisation that trusts the CI/CD platform. From the hub, a pipeline hops to a spoke role in whichever account it deploys to. This enables three outcomes:
- Pipelines hold no long-lived AWS keys, only credentials issued for each run, so there's nothing to leak, rotate or revoke.
- A new repository can deploy to the accounts it's allowed to, and a new account can be reached by every pipeline, without trust being set up for each combination of repository and account.
- Trust is defined in one place, so tightening it once tightens it for the whole estate.
Motivation
Letting AWS accept a CI/CD platform's OIDC tokens removes long-lived keys. The most direct way to set it up, and the one walked through by GitHub's, GitLab's and AWS's own guides, is to give each repository a role of its own in each account it deploys to, with a trust policy that accepts only that repository's tokens. That works for a handful of repositories, but as the estate grows it fails in three ways:
- Setup multiplies. The number of roles grows with repositories multiplied by accounts, and each one is set up by hand whenever either side grows.
- Trust is scattered. Each account holds its own trust policies, the rules that say who may assume each role, for its own list of repositories, so nobody can see in one place which pipelines can reach which accounts.
- Tightening trust becomes a project. Changing who may deploy, such as limiting production to a protected branch, means editing trust policies in every account.
This pattern puts a single trusted entry point in front of every account, in a hub-and-spoke shape:
- The hub is a role in one central account, such as the management account that runs the organisation itself, or an account set aside for identity. It's the only thing in the organisation that trusts the CI/CD platform, it accepts tokens only from the organisation's own repositories, and its only right is to assume spoke roles, because any right the hub held would be held by every pipeline in the organisation.
- Each workload account, one of the accounts that run your systems, has one or more spoke roles, each trusting only the hub. An account can have a single spoke for every deployment, or several, each giving only the rights one kind of deployment needs.
- A pipeline exchanges its OIDC token for the hub, then reaches a spoke through role chaining. Each session is named after the repository that started it, so every account's audit log shows which repository made each change.
The two variants below differ in how many hubs the organisation trusts.
Applicability
Use this pattern when:
- Pipelines in many repositories deploy into several accounts of an AWS Organization.
- Pipelines should hold no long-lived AWS keys.
- A new repository, or a new account, should be able to deploy without trust being set up for each combination of repository and account.
Do not use this pattern when:
- There's a single account or a single repository, where one OIDC role in that account is simpler.
Structure
Variant A: One hub
When every account can be reached under the same rules, such as by any pipeline in the organisation's own repositories, this variant works best.
- One hub trusts the CI/CD platform for the whole organisation.
- Every spoke role, in every account, trusts that hub.
Variant B: A hub per trust boundary
When some accounts, such as production, need stronger protection than the rest, this variant works best.
- Production has a hub of its own, which only trusts pipelines running from a protected branch or a protected deployment environment.
- Production's spoke roles trust only that hub, so no other pipeline can reach them, while the rest of the estate keeps its own hub.
Participants
| Participant | Role |
|---|---|
| Pipeline | Runs in any repository on the CI/CD platform, and exchanges the OIDC token the platform issues it for the hub role. |
| CI/CD platform | Issues each run a short-lived OIDC token that names the repository, branch and deployment environment it ran from. |
| OIDC provider | Registers the CI/CD platform with AWS once, in the hub's account, so AWS accepts its tokens, as an IAM OIDC identity provider. |
| Hub role | Trusts tokens only from the organisation's own repositories, and may only assume spoke roles. |
| Spoke roles | Give pipelines the rights they need in their own account, one or more per account, each trusting only the hub. |
| Audit log | Records each session under the name of the repository that started it, in every account, such as AWS CloudTrail. |
Constraints and trade-offs
- Everything rests on the hub's trust policy. Centralising trust means a single mistake in that policy, such as accepting tokens from any repository on any branch, opens every account at once, so the policy needs the same review as any change to production.
- A pipeline can't create the first role it needs to reach a new account, so each new account's first spoke is created in a one-off step outside the pipelines. Further spokes can then be managed as code, through it.
- Role chaining limits each session to one hour, so a deployment that runs for longer has to request fresh credentials part way through.
- A spoke with broad rights is simple to start with, but a compromised pipeline can then do anything that spoke allows in its account, so spokes need narrowing, or splitting by kind of deployment, once the services each account uses have settled.
Variant A: One hub trade-offs
- The core benefit is simplicity: one trust policy to review, and one place to tighten.
- The core cost is that every account shares the same entry rules, so protecting production more strictly means narrowing what reaches every account. Moving to a hub per trust boundary later means pointing every pipeline that deploys to production at the new hub.
Variant B: A hub per trust boundary trade-offs
- The core benefit is that production's rules can be strict without slowing down every other account.
- The core cost is a second trust policy to keep in step, and pipelines that need to know which hub leads to which account.
Part of how I deliver
This is how I give every pipeline access to the AWS accounts it needs, with no long-lived keys and no trust to set up by hand. It's one of many patterns I bring through contract delivery, and it lets a new repository deploy on its first day.
Are your engineers waiting on infrastructure?
I build infrastructure and pipelines your engineers serve themselves from, with your standards enforced in one place. If releases are slow, risky or queued behind one specialist, book a call and we'll talk it through.
