Eliminating Long-Lived Cloud Secrets via GitHub Actions OIDC Federation
Stop storing long-lived cloud keys in GitHub Secrets. Learn how to use OIDC federation to exchange short-lived GitHub tokens for temporary cloud credentials.
27 Aug 2025, 04:01 UTC

The Secret Sprawl Problem
Many GitHub Actions workflows authenticate to cloud providers using long-lived access keys stored as repository secrets. These keys are high-value targets: they rarely rotate, lack granular identity context, and provide permanent access until manually revoked. If a secret is leaked via a compromised dependency or an accidental log dump, the attacker gains persistent access to the cloud environment.
The solution is to move from static secrets to identity federation using OpenID Connect (OIDC). Instead of storing a key, GitHub provides a short-lived, cryptographically signed token that proves the identity of the specific workflow run. The cloud provider verifies this token and issues temporary credentials that expire automatically.
Architecture Requirements
- Identity Provider: GitHub.com or a GitHub Enterprise Server instance with OIDC enabled.
- Relying Party: A cloud provider supporting OIDC (e.g., AWS IAM Roles, GCP Workload Identity Federation, or Azure Federated Identity).
- Workflow Permissions: The job must have
id-token: writepermissions to request the token from GitHub's token service.
OIDC (OpenID Connect) is an identity layer on top of the OAuth 2.0 protocol. It allows GitHub to vouch for the identity of a workflow—including the repository, the actor, and the git ref—without sharing a password or key.
The Smallest Suitable Design
Avoid creating a single "super-role" for the entire organization. The most secure minimal design uses one cloud IAM role per environment (e.g., prod-deploy-role, staging-deploy-role). Each role is configured with a trust policy that explicitly limits which GitHub repositories and branches can assume it.
Trust and Data Boundaries
The trust boundary is defined by the claims inside the OIDC token. A secure trust policy should not just check the organization, but the specific repository and the environment or branch. For example, a production role should only trust tokens where the sub (subject) claim matches repo:org/repo:ref:refs/heads/main.
Implementation Example (AWS)
To implement this, first configure the OIDC Identity Provider in the cloud console. Then, update your workflow file (e.g., .github/workflows/deploy.yml) to request the token and assume the role.
name: Cloud Deployment
on:
push:
branches: [ main ]
permissions:
id-token: write # Required to request the OIDC JWT
contents: read # Required for checkout
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-prod
aws-region: us-east-1
- name: Verify Identity
run: aws sts get-caller-identity
Execution Details: Run this on a GitHub-hosted runner. The aws-actions/configure-aws-credentials action handles the exchange of the GitHub JWT for temporary AWS STS credentials automatically. Ensure the IAM role's trust policy is scoped to your specific repository to prevent "confused deputy" attacks where other repositories in your org might try to assume the role.
Operational Checks and Failure Modes
Verification Steps
- Positive Test: Run the workflow from the
mainbranch and verify theaws sts get-caller-identityoutput matches the assumed role. - Negative Test: Attempt to run the same workflow from a feature branch or a fork. The cloud provider should return an
AccessDeniedorInvalidIdentityTokenerror. - Audit Trail: Check the cloud provider's audit logs (e.g., AWS CloudTrail). The log should show a
AssumeRoleWithWebIdentityevent containing the GitHub repository and workflow run ID.
Failure Modes
| Failure Scenario | Cause | Resolution |
|---|---|---|
| Token Request Failed | Missing id-token: write permission |
Add permission to the job or workflow level. |
| Role Assumption Denied | Trust policy mismatch (e.g., wrong branch name) | Verify the sub claim in the trust policy matches the GitHub ref. |
| Credential Expiry | Job exceeds token validity window | Break long-running jobs into smaller steps or refresh credentials. |
When to Change This Design
This OIDC-based approach is sufficient for most CI/CD needs. However, you should reconsider this architecture if:
- Cross-Account Complexity: You need to assume roles across multiple cloud accounts with complex chaining that exceeds OIDC session limits.
- Hardware Requirements: Your security policy requires hardware-bound identities (HSMs) that cannot be satisfied by software-signed JWTs.
- Self-Hosted Runners: You use self-hosted runners in an environment where the runner cannot reach the GitHub OIDC token endpoint.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.