Stashing long-lived AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in your GitHub repository secrets is a ticking security time bomb. If those credentials leak, an attacker gains persistent access to your infrastructure until you manually rotate them. Implementing OIDC (OpenID Connect) in GitHub Actions changes the game by allowing your workflows to request temporary, short-lived tokens directly from AWS.
By moving to a passwordless architecture, you reduce the attack surface of your CI/CD pipeline and simplify secret management. You no longer need to track expiration dates for IAM user keys or worry about rotating secrets across dozens of repositories. This guide provides a step-by-step implementation for setting up this modern authentication flow using the latest AWS and GitHub standards.
TL;DR — Stop using static IAM keys. Use OpenID Connect (OIDC) to let GitHub Actions assume an IAM role dynamically. This requires creating an OIDC Identity Provider in AWS, configuring a trust policy, and updating your YAML workflow permissions to id-token: write.
Understanding OIDC and GitHub Actions
💡 Analogy: Think of static IAM keys like a physical master key to your house that never changes. If you lose it, you have to change all the locks. OIDC is like a smart lock that issues a one-time digital key to a delivery person. The key only works for 30 minutes, and the lock only grants it after verifying the person's digital ID badge from a trusted employer.
OpenID Connect (OIDC) is an identity layer on top of the OAuth 2.0 protocol. In the context of GitHub Actions, it allows GitHub to act as an Identity Provider (IdP). When your workflow runs, GitHub generates a unique JWT (JSON Web Token) that contains metadata about the job, such as the repository name, the actor, and the branch. This token is sent to AWS Security Token Service (STS).
AWS verifies this token against the OIDC Identity Provider configuration you've established in your account. If the token is valid and matches the trust relationship defined in your IAM role, AWS returns short-lived credentials. These credentials typically expire after one hour, ensuring that even if they are intercepted, their utility to an attacker is extremely limited. This process happens entirely in the background during the configure-aws-credentials step of your workflow.
When to Use Passwordless Authentication
You should adopt OIDC for GitHub Actions in almost every production-grade environment. It is particularly critical for organizations managing multi-account AWS structures where distributing and rotating IAM keys across different teams becomes an operational nightmare. If you are currently storing AWS secrets at the organization or repository level in GitHub, you are a prime candidate for this upgrade.
Another specific scenario is compliance-heavy industries. Frameworks like SOC2 and ISO 27001 emphasize the principle of least privilege and credential rotation. OIDC naturally fulfills these requirements by ensuring that no permanent "secret" exists in the GitHub ecosystem. Furthermore, if you use ephemeral runners or GitHub-hosted runners, OIDC removes the need to worry about where those secrets are stored in memory or on disk between jobs.
Step-by-Step OIDC Implementation
Step 1: Create the OIDC Identity Provider in AWS
First, you must tell AWS to trust GitHub as an identity issuer. This is a one-time setup per AWS account. Use the AWS Console or Terraform to create the provider with the following details:
- Provider URL:
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com
Step 2: Configure the IAM Role and Trust Policy
Create an IAM role that your GitHub Action will assume. The critical part is the Trust Policy. This policy defines exactly which GitHub repositories are allowed to assume the role. Replace <ORG> and <REPO> with your specific details.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:<ORG>/<REPO>:*"
},
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
}
]
}
Step 3: Update GitHub Actions Workflow YAML
In your .github/workflows/deploy.yml, you must grant the job permission to request an OIDC token. Then, use the aws-actions/configure-aws-credentials@v4 action to exchange that token for AWS credentials.
name: AWS Deployment
on:
push:
branches: [ main ]
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/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/my-github-actions-role
aws-region: us-east-1
- name: Verify Authentication
run: aws sts get-caller-identity
Common Pitfalls and Troubleshooting
⚠️ Common Mistake: Forgetting the id-token: write permission. If this is missing, GitHub will not provide a JWT to the environment, and the AWS action will fail with a "Could not find a valid OIDC token" error.
Another frequent issue involves the sub (subject) claim in the IAM trust policy. GitHub's OIDC subject format is very specific: repo:<org>/<repo>:ref:refs/heads/<branch>. If you use repo:<org>/<repo>:*, it allows any branch or tag in that repo to assume the role. If you need stricter security, ensure you specify the branch or environment explicitly in the condition block.
When I first implemented this with v4 of the AWS credentials action, I noticed intermittent "Expired Token" errors. This usually happens if the system clock on a self-hosted runner is out of sync. For GitHub-hosted runners, this is rarely an issue, but ensure you are using the latest version of the action to take advantage of improved retry logic and token handling.
Advanced Security Tips and Summary
To maximize the security of your OIDC setup, follow these expert-tested strategies:
- Scoping by Environment: If you use GitHub Environments (e.g., "production"), your
subclaim can be scoped torepo:<org>/<repo>:environment:production. This prevents code on feature branches from accidentally deploying to production. - Minimize Permissions: The IAM role assumed by GitHub should follow the principle of least privilege. Do not attach
AdministratorAccess. Use specific policies for S3, Lambda, or ECS. - Audit Logs: Use AWS CloudTrail to monitor
AssumeRoleWithWebIdentityevents. You can see exactly which GitHub workflow run triggered an authentication request.
📌 Key Takeaways
- OIDC eliminates the need for static, long-lived AWS secrets in GitHub.
- Authentication is based on a trusted relationship between GitHub and AWS IAM.
- Permissions for
id-token: writemust be explicitly set in your workflow. - Always use the
subclaim to restrict access to specific repositories and branches.
Frequently Asked Questions
Q. How to authenticate GitHub Actions to AWS using OIDC?
A. You authenticate by creating an OIDC Identity Provider in AWS, setting up an IAM role with a trust policy pointing to that provider, and then using the aws-actions/configure-aws-credentials action in your workflow with id-token: write permissions enabled.
Q. What are the benefits of using OIDC for GitHub Actions?
A. The primary benefit is security. OIDC uses short-lived tokens (JWTs) instead of permanent access keys. This eliminates the risk of credential leakage, simplifies key rotation, and provides granular control over which repositories and branches can access AWS resources.
Q. Is OIDC better than an IAM user for GitHub Actions?
A. Yes, OIDC is significantly more secure. Using an IAM user requires generating static credentials that must be stored and manually rotated. OIDC is a passwordless flow that generates temporary credentials on-the-fly, reducing management overhead and minimizing potential security breaches.
Post a Comment