AWS Credentials in GitHub Actions: A Secure OIDC Solution
Learn how to stop hardcoding AWS credentials in GitHub Actions by implementing a secure OpenID Connect (OIDC) solution using AWS CDK.
Picture this: you're reviewing a security incident report where an attacker gained access to your production AWS environment through compromised GitHub repository secrets. The AWS access keys, sitting there as plain text in your repository settings, provided the golden ticket to your entire infrastructure. Sound familiar? You're not alone.
The Hidden Dangers of Hardcoded Credentials
Every day, thousands of developers store AWS Access Keys and Secret Keys as GitHub repository secrets, unknowingly creating a ticking time bomb in their CI/CD pipelines. These long-lived credentials become persistent attack vectors that can haunt your infrastructure for months or even years.
When you hardcode credentials, you're essentially handing out permanent keys to your AWS kingdom. Unlike the temporary badges you might use at work, these credentials don't expire until you manually rotate them—and let's be honest, how often does that really happen? Meanwhile, they accumulate increasingly broad permissions as teams rush to fix broken deployments, creating a perfect storm of security vulnerabilities.
The operational burden is equally problematic. Manual key rotation becomes an error-prone process that teams frequently postpone or forget entirely. Permission creep sets in as developers add more and more privileges to make deployments work, often without understanding the full scope of access they're granting. And when something goes wrong, tracing which credentials were used when becomes a nightmare that even the most detailed audit logs struggle to illuminate.
Enter OpenID Connect: The Modern Authentication Solution
Fortunately, there's a better way. Instead of storing static credentials, GitHub Actions can securely authenticate with AWS using OpenID Connect (OIDC) tokens. This approach fundamentally changes how your CI/CD pipelines interact with cloud resources.
Here's how it works: when your GitHub Action runs, GitHub generates a unique, short-lived token specifically for that workflow execution. This token contains verifiable information about the repository, branch, and workflow context. AWS then validates this token against a configured OIDC provider and issues temporary credentials that automatically expire when the workflow completes.
The beauty of this system lies in its ephemeral nature. There are no permanent credentials to steal, no keys to rotate, and no secrets to accidentally expose in logs. Each workflow run gets its own isolated authentication context that dies with the workflow.
Building a Production-Ready OIDC Solution
Let me walk you through a real-world implementation that I've developed to solve these authentication challenges. This CDK project creates a secure, scalable OIDC integration that eliminates the need for hardcoded credentials entirely.
The heart of the solution starts with establishing trust between GitHub and AWS. In the main application file, we configure an OpenID Connect provider that serves as the bridge between these two systems:
// src/app.ts
const provider = new OpenIdConnectProvider(this, "github-provider", {
clientIds: ["sts.amazonaws.com"],
url: "https://token.actions.githubusercontent.com",
thumbprints: [
"6938fd4d98bab03faadb97b34396831e3780aea1",
"1c58a3a8518e8759bf075b76b750d4f2df264fcd",
],
});
Those thumbprints might look like cryptic strings, but they're actually critical security components. GitHub uses cross-signed certificates, which means either of two possible intermediary certificates could be presented during the SSL handshake. By including both thumbprints, we ensure that AWS can validate GitHub's identity regardless of which certificate path is used—a subtle but crucial detail that prevents authentication failures.
The real magic happens in how we create repository-specific access controls. Rather than granting broad permissions to all repositories, the system creates isolated IAM roles for each repository:
// src/constructs/actions-oidc.ts
const principal = new OpenIdConnectPrincipal(provider).withConditions({
StringLike: {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": `repo:${userName}/${repoName}:*`,
},
});
This conditional access control is where the security rubber meets the road. The sub (subject) condition ensures that only workflows running from a specific repository can assume the role. Even if someone gains access to your GitHub organization, they can't use credentials intended for one repository to access resources meant for another.
The permission structure itself follows the principle of least privilege, granting only the specific AWS actions needed for CDK deployments:
// src/common.ts
export const cdkPolicyStatements = [
new PolicyStatement({
effect: Effect.ALLOW,
actions: ["sts:AssumeRole", "iam:PassRole"],
resources: ["arn:aws:iam::*:role/cdk*"],
}),
new PolicyStatement({
effect: Effect.ALLOW,
actions: ["s3:*"],
resources: ["arn:aws:s3:::cdk*"],
}),
// Additional CDK-specific permissions...
];
Notice how the resources are scoped to CDK-specific patterns. This isn't an accident. It's a deliberate design choice that prevents workflows from accessing resources outside their intended scope. If a workflow tries to access an S3 bucket that doesn't start with "cdk", it will be denied access.
The architecture scales elegantly as your organization grows. Instead of managing individual credential pairs for each repository, you can define repository classes that inherit from a common base:
// src/app.ts
const MANAGED_REPOSITORIES = [ThisOIDCStack, AnotherProjectStack];
MANAGED_REPOSITORIES.map((Repository) => new Repository(this, provider));
Each repository gets its own managed role with tailored permissions, but they all share the same underlying OIDC provider. This reduces operational overhead while maintaining security isolation between projects.
Security Benefits
No Stored Secrets
- Zero long-lived credentials in GitHub
- Tokens are ephemeral and automatically expire
- No credential rotation required
Principle of Least Privilege
- Repository-specific access controls
- Time-limited token validity
- Conditional access based on workflow context
Audit Trail
- Complete CloudTrail logging of all actions
- Token-based attribution to specific workflows
- No shared credential usage
Compliance Ready
- Meets SOC 2 and ISO 27001 requirements
- Supports automated compliance checks
- Eliminates credential storage risks
Implementation Steps
Deploy the CDK Stack
npm install npx cdk deployConfigure GitHub Workflow
name: deploy run-name: Deploy 🚀 on: push: branches: - main permissions: id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest env: CDK_DEFAULT_ACCOUNT: ${{vars.CDK_DEFAULT_ACCOUNT}} CDK_DEFAULT_REGION: ${{vars.CDK_DEFAULT_REGION}} steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: run_install: false - uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' - name: Install run: pnpm install - name: Assume role using OIDC uses: aws-actions/configure-aws-credentials@master with: aws-region: ${{vars.CDK_DEFAULT_REGION}} role-to-assume: arn:aws:iam::${{vars.CDK_DEFAULT_ACCOUNT}}:role/${{vars.APP_NAME}}-github-ci-role - name: Deploy run: npx aws-cdk deploy --verbose --require-approval never
Conclusion
OIDC integration with GitHub Actions represents a significant security improvement over traditional credential storage. By eliminating long-lived credentials, implementing granular access controls, and providing complete audit trails, this solution addresses the fundamental security challenges of CI/CD authentication.
The CDK implementation presented here provides a production-ready foundation that can be customized for your specific requirements while maintaining security best practices.
Key Takeaways:
- OIDC tokens are more secure than stored credentials
- Repository-specific access controls prevent lateral movement
- Automated credential management reduces operational overhead
- Comprehensive testing ensures reliable security controls
Ready to modernize your CI/CD security? Start by deploying this CDK solution and gradually migrating your workflows to use OIDC authentication.
This implementation is open source and available at github-oidc-cdk. Contributions and feedback are welcome!