Workload identity federation: removing the secret without removing the question
Keyless authentication was the right move and it moved the problem rather than removing it. The secret is gone; the authority is now expressed in code, and code is changed by pull request.
What does workload identity federation actually remove?
The static secret, which is a real improvement. What it does not remove is the question: the trust policy deciding which repository, branch or environment may assume a powerful role is now the credential, and it is frequently written once and never reviewed.
- Federation replaces a bearer secret with a claim set evaluated against a trust policy. The security property moves from secret custody to policy correctness.
- Four trust policy failure modes account for most misconfigurations, and wildcards on branch or workflow are the dominant one.
- Trust policy changes are privileged configuration changes and should carry human approval bound to the policy content.
Part of Machine and signing identity
What federation actually does
A CI pipeline needs to deploy to a cloud account. Historically that meant a long-lived access key stored as a pipeline secret — a bearer credential, exfiltrable, frequently leaked.
Workload identity federation replaces it. The CI platform issues a short-lived OIDC token asserting claims about the workload — which repository, which branch, which workflow, which environment. The cloud provider evaluates those claims against a trust policy and, if they match, issues short-lived credentials.
No static secret exists. That is a real and substantial improvement and nothing here argues against adopting it.
Where the security property now lives
It has moved, and the move is worth naming precisely.
| Static secret | Federation | |
|---|---|---|
| What must be protected | The secret value | The trust policy |
| How it is compromised | Exfiltration | Misconfiguration or unauthorised change |
| Who can compromise it | Anyone who reads the secret | Anyone who can merge to the policy |
| Detection | Secret scanning | Code review, if anyone looks |
| Blast radius | The credential's permissions | The role's permissions, for anyone matching the policy |
The last row is the one that surprises people. A permissive trust policy on a powerful role is worse than a leaked key, because it is a standing invitation rather than a single compromised credential.
Four failure modes
- Wildcard subject. A policy matching any branch or any workflow in a repository. A pull request from a fork, or a branch an attacker can create, then assumes the role.
- Missing audience check. A policy that does not constrain the audience claim, permitting tokens issued for a different purpose to be replayed.
- Over-broad repository match. A policy scoped to an organisation rather than a repository, so any repository in the org can assume a production role.
- Unreviewed policy change. The policy lives in infrastructure code and is changed in a pull request that reviewers approve as routine.
The first is the most common and the most exploited. The fourth is the one that makes the other three persistent, because it is how a tightened policy becomes loose again six months later.
Auditing your policies
A short exercise with a clear output.
- Export every trust policy attached to a role with write access to production.
- For each, extract the subject condition. Flag any containing a wildcard in the branch, workflow or environment position.
- Check the audience condition is present and constrained.
- For each flagged policy, determine who can create a matching branch or workflow. That set is who can assume the role.
- Report the size of that set per production role. It is usually much larger than the team that owns the role.
Approval bound to the policy content
Since the trust policy is the security boundary, a change to it should carry human approval bound to what changed — not a pull request approval on a diff nobody read carefully among forty other lines of Terraform.
{
"type": "manav-stmt/1",
"action": "trust_policy_change",
"render": [
"Role: [arn / resource name] Permissions: [summary]",
"Current subject condition: [value]",
"Proposed subject condition: [value]",
"Wildcard present: [yes/no] Audience constrained: [yes/no]",
"Principals able to match after change: [count, description]"
],
"approver": "[credential assertion]"
}
The last render line is the one that changes decisions. An approver seeing after this change, 340 repositories can assume this production role approves differently from one seeing a Terraform diff.
What federation still leaves open
Even with a correct policy, a compromised pipeline running in an authorised repository on an authorised branch obtains legitimate credentials. Federation authenticates the workload, and a compromised workload is still the workload.
That residual is addressed at the action layer — human approval bound to consequential deployments — rather than at the credential layer, and it is the reason federation should be treated as one control rather than the answer.
Where federation trust policies go wrong
| Pattern | Effect |
|---|---|
| Repository wildcard | Any repository in the org can assume the role |
| No branch condition | Any branch, including one an attacker can create |
| No environment condition | A test workflow assumes a production role |
| Pull request context permitted | A fork's PR can assume the role |
The last is the one that has produced real incidents. Federation makes the subject claim easy to get almost right, and almost right is the failure mode.
Objections and honest limits
“Federation is strictly better than secrets.” On the theft axis, clearly. On the breadth axis it can be worse, because a static secret has an obvious owner and a trust policy feels like configuration rather than a credential.
“We reviewed the policy when we set it up.” Which is when repositories, branches and workflows were different. A trust policy is a credential and should be reviewed like one.
Auditing federation trust policies
- List every role assumable by federation. And what each can reach.
- Check for wildcards in the subject claim. Repository, branch and environment.
- Exclude pull request context from powerful roles. A fork should not reach production.
- Review on the same cadence as secrets. It is a credential, not configuration.
Terms used here
- Workload identity federation
- Exchanging a build system's OIDC token for cloud credentials, removing the need for a stored secret.
- Subject claim
- The identifier in the token describing the workflow — the string a trust policy matches against.
- Trust policy
- The rule deciding which subjects may assume a role. In federation, this is the credential.
Frequently asked questions
Should we not adopt federation? Adopt it. It removes an entire class of leaked-credential incidents. The point is that it relocates the security property to the trust policy, which then needs the attention the secret used to get.
Are wildcards ever acceptable? In non-production roles with narrow permissions, sometimes. On a role with production write access, treat a wildcard as a finding.
Does this apply to all cloud providers? The mechanism differs in detail across providers but the structure — claims evaluated against a policy — and the failure modes are common to all of them.
How do we stop policies loosening over time? Bind approval to the policy content with the resulting principal count rendered, so loosening is a visible decision rather than a line in a diff.
Is federation better than static secrets? On theft, clearly. On breadth it can be worse, because a trust policy feels like configuration rather than a credential.
What is the most dangerous pattern? Permitting pull request context on a powerful role, which can let a fork's workflow assume it.
How often should trust policies be reviewed? On the same cadence as secrets. A trust policy is a credential, and repositories and workflows change.
Where this fits in Manav
Manav anchors machine and agent authority to a named human through a signed delegation with scope, depth and enforced revocation — so an action at hop four still resolves to a person.