Teams that migrate GitHub Actions off long-lived AWS access keys and onto OIDC federation often stop at "we removed the keys" without checking whether the trust policy they wrote actually restricts which workflow runs can assume the role. A trust policy scoped only to the repository, without environment or branch conditions, can let a pull request from a fork assume a production-access role — which defeats much of the point of eliminating the static credential in the first place.
The Trust Policy Most Teams Ship First
A basic OIDC trust policy looks like this, and it's a real improvement over static keys, but it's more permissive than it looks:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<account>:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"},
"StringLike": {"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"}
}
}]
}
The sub condition here matches any ref in the repository — any branch, any pull request, including one opened from a fork. If the repository accepts external contributions, a malicious fork PR that modifies the workflow file can potentially assume that role during CI, depending on how the workflow triggers are configured.
Scoping to GitHub Environments, Not Just Repositories
GitHub Environments add a protection layer OIDC trust policies should actually depend on: required reviewers, deployment branch restrictions, and wait timers. The sub claim can be scoped to a specific environment rather than just the repository:
{
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:environment:production"
}
}
}
Combined with a GitHub Environment configured to require manual approval and restrict deployment to the main branch, this closes the fork-PR gap: a workflow run only gets the environment:production subject claim if it's actually executing a job targeting that protected environment, which fork PRs can't trigger without an approver's sign-off.
Handling Multiple Environments Cleanly
Most teams need at least a staging and production distinction, each with its own IAM role and blast radius:
# .github/workflows/deploy.yml
jobs:
deploy-prod:
environment: production
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::<account>:role/github-actions-prod-deploy
aws-region: us-east-1
Each environment gets a distinct IAM role scoped to only the resources that deployment target needs — a staging deploy role should not have the same permission boundary as production, even though both authenticate through the same OIDC provider.
What This Prevents
Beyond the fork-PR risk, environment-scoped trust policies also limit lateral movement if a workflow file itself is compromised through a supply-chain issue in an Action dependency — the compromised workflow can only assume the role tied to whatever environment it's actually deploying to, not every role the repository happens to reference anywhere in its workflow history.
Frequently Asked Questions
Does this apply to GitLab CI or other CI/CD platforms too?
The specific sub claim syntax is GitHub-specific, but the underlying principle — scope the trust policy to the narrowest deployment context available, not just the repository — applies equally to GitLab CI, CircleCI, and other OIDC-capable CI/CD platforms.
How do we audit whether our existing trust policies have this gap?
Review every OIDC trust policy's sub condition for a wildcard or repository-only scope without an environment: qualifier, then cross-reference against which repositories accept external contributions or have permissive workflow triggers on pull_request_target.
Does environment scoping add friction for legitimate deploys?
Minimal — the environment approval step is typically a single click for an authorized reviewer, and it only applies to the environments you've configured to require it, not every workflow run.
What if we don't accept external contributions at all?
The fork-PR risk is lower for fully private repositories with no external contributors, but environment scoping is still good practice for limiting blast radius if a maintainer's account or a dependency is compromised.
Talk to us about your CI/CD credential elimination pilot → rutagon.com/contact or call 907-841-8407.