CI Secrets and Workload Identity
Use short-lived workload credentials where the destination supports them, and restrict which jobs may obtain which privileges. Replacing a stored cloud key with federation reduces one credential-management burden; it does not make every workflow, artifact, or runner trusted.
Separate untrusted code validation from privileged release work. The central question is not only “Where is the secret stored?” but “Which code can run while this authority is available?”
TL;DR
- Inventory stored credentials and the exact operation each one authorizes.
- Prefer a supported short-lived identity exchange over a reusable deployment key when it meets the destination's requirements.
- Bind trust to the intended issuer, audience, repository identity, and permitted workflow/ref/environment context.
- Keep untrusted contribution code away from privileged jobs, runner state, and release credentials.
- Verify artifact origin and content before a privileged deployment or publication step.
- Test denied cases and revocation paths; a successful deployment alone does not validate a trust policy.
Quick Example
This complete Python policy model checks selected fictional job claims. It assumes a separate, trusted verifier has already validated the actual token's signature, issuer, audience, expiry, and required protocol conditions. It does not implement OIDC, parse tokens, configure a cloud role, or grant access.
Save it as trust_model.py and run python trust_model.py. The identifiers are fictional and the conditions are illustrative. Real providers differ in supported claims and policy syntax. An environment-based subject is not automatically the same thing as a branch restriction; verify the issuer's claim behavior and enforce the intended combination in the supported destination policy.
Core Concepts
Federation exchanges job identity for scoped authority
GitHub's OIDC overview describes workflows exchanging identity for short-lived cloud credentials. The destination's trust configuration decides whether the presented job identity qualifies. The resulting role or credential policy decides what operations are allowed.
Keep these decisions separate. Correctly identifying a job does not justify broad administrative permissions. Conversely, a narrowly scoped role can still be exposed to the wrong job by an overbroad trust condition.
Claims need a supported trust policy
Match the intended issuer and audience, then constrain the permitted repository and execution context. Verify the meaning of branch, environment, event, and reusable-workflow claims in the actual issuer and provider. Use stable repository identifiers where the design and provider support them instead of relying only on names that can change.
GitHub's reusable-workflow OIDC guidance explains workflow claims and provider support differences. A claim being present in a token does not establish that a destination policy can evaluate it in the way your design assumes.
Untrusted execution is a separate boundary
A build may execute dependency scripts, test helpers, generated tools, and source-controlled commands. Treat the trust of that code as an input to the job design. Do not give a pull-request test job release privileges merely because the repository normally publishes a package.
GitHub's secure-use reference covers least privilege, untrusted code checkout, third-party actions, and runner risks. Read the applicable event behavior before combining outside contributions with a privileged context.
Follow authority through the pipeline
This is an editorial review map, not a provider-neutral deploy policy. Fill it using the actual CI system, artifact service, identity provider, and destination. Keep the proof of artifact origin distinct from proof that the artifact is safe to deploy.
Migrate one credential safely
Start with one operation and record its current authority and consumers. Identify the job, repository, destination, environment, and required resources. Remove permissions unrelated to that operation before treating the exchange as complete.
Implement the supported identity flow in a disposable or staging environment. Test a permitted job and representative denials: wrong repository, wrong audience, unauthorized ref, unapproved environment, and missing claims as applicable. Test the destination's actual policy rather than relying on the Python model above.
Review the artifact handoff into the privileged job. Avoid executing an untrusted helper or build script after obtaining deployment credentials. If a privileged job consumes a prior artifact, verify the expected origin, revision, and content boundary through the supported mechanism.
Only retire the old reusable credential after the required consumers have moved and the new flow's failure and recovery behavior is understood. Verify that the old credential no longer works according to the intended revocation process. Do not leave an undocumented permanent fallback that restores the original exposure.
Best Practices
Minimize ambient authority
Give each job only the credentials and repository permissions it needs. Separate validation, packaging, publication, and deployment when their trust levels differ. Restrict where a runner can persist state between jobs.
Pin and review executable dependencies
Use the CI system's supported immutable references and a deliberate update process for actions and tools. Review changes to workflow code as changes to the execution boundary, not ordinary prose edits.
Keep credentials out of diagnostics
Avoid printing raw tokens and structured secret blobs. Automatic masking is not a guarantee for every transformation. Retain safe identifiers and audit context sufficient to explain what ran and which authority it used.
Common Mistakes
Trusting an entire organization without a need
Bad: Allow any repository or branch to assume a powerful deployment role.
Correct: Constrain the intended workload identity and reduce the role to the required resources and actions.
Running untrusted code after credential exchange
Bad: Obtain release authority and then execute a helper from an unreviewed contribution.
Correct: Keep the privileged job's executable inputs trusted and verify the artifact boundary before release.
Treating short lifetime as sufficient protection
Bad: Accept broad privileges because the credential expires soon.
Correct: Combine short lifetime with narrow trust, least privilege, isolation, and denied-case tests. Harmful actions can happen well before expiry.
FAQ
Does OIDC remove all secrets from CI?
No. It can replace selected reusable credentials where supported. Other integrations, signing material, or application secrets may remain and need their own lifecycle and scope.
Is a private repository's pull request trusted?
Repository visibility alone does not establish the trust of every contributor, dependency, or executed change. Review the event and source permissions before exposing authority.
Can every cloud evaluate every claim?
No. Check provider support and the issuer's documented claim format. A generic example is not a deployable policy for every destination.
What should be tested first?
The exact permitted job and the cases that should be denied. Then verify artifact handoff, least privilege, and the process for stopping access or recovering a release.
Related Topics
- CI/CD — Build, check, and release boundaries.
- GitHub Actions Security — Workflow and runner controls.
- Secrets Management — Credential access and rotation.
- npm — Separate package validation and publishing authority.