Secretless CI/CD with GitHub Actions OIDC

Managing AWS credentials for CI/CD pipelines can quickly become complex and risky. Hardcoding AWS credentials or storing them as GitHub Secrets exposes your organization to credential leakage, auditability challenges, and significant security risks. Similarly, statically binding IAM roles to runners complicates scaling, limits flexibility, and creates management overhead — especially in multi-account environments. In this tutorial, you'll learn how to securely manage AWS access from GitHub Actions using AWS Attribute-Based Access Control (ABAC), GitHub Actions OIDC tokens, and Amazon Cognito as an identity broker — all without static credentials or brittle IAM bindings. This model maps OIDC claims from your CI/CD workflows to a short-lived AWS STS token. This model can be extended to any service provider that you need to connect to from your CI/CD workflows, as long as the service provider is OIDC compliant and supports Identity Federation. Why Avoid Hardcoded Credentials and Static IAM Bindings? Security Risks: Hardcoded secrets can leak through logs, repos, or misconfigurations. Auditability Issues: Static credentials obscure who performed actions and when. Operational Bottlenecks: Static role-to-runner bindings make it hard to scale access across apps or environments. If you’re scaling GitHub Actions across multiple teams, apps, or accounts — you need a better way. A least privileged access model along with dynamic infrastructure scaling. Secure, Scalable Architecture Overview This architecture connects GitHub Actions to AWS securely using temporary session credentials based on identity claims and session tags. GitHub Actions → [OIDC Token] ↓ Cognito Identity Pool → [Maps Claims → Principal Tags] ↓ AWS STS → [AssumeRoleWithWebIdentity → Session with Tags] ↓ AWS Resources (enforced via ABAC) Step-by-Step Setup 1. Create an OIDC Identity Provider for GitHub Resources: GitHubOIDCProvider: Type: AWS::IAM::OIDCProvider Properties: Url: https://token.actions.githubusercontent.com ClientIdList: - "sts.amazonaws.com" ThumbprintList: - "6938fd4d98bab03faadb97b34396831e3780aea1" You can also configure this manually via the IAM Console → Identity Providers. 2. Configure Cognito Identity Pool Map OIDC claims from GitHub into principal tags. Resources: IdentityPool: Type: AWS::Cognito::IdentityPool Properties: IdentityPoolName: GitHubActionsABAC AllowClassicFlow: true OpenIdConnectProviderARNs: - !GetAtt GitHubOIDCProvider.Arn PrincipalTags: repository: "repo:repository" environment: "repo:environment" workflow: "repo:job_workflow_ref" IdentityPoolRoleAttachment: Type: AWS::Cognito::IdentityPoolRoleAttachment Properties: IdentityPoolId: !Ref IdentityPool Roles: authenticated: !GetAtt ABACRole.Arn 3. Define the ABAC IAM Role This IAM role uses session tags for authorization. Resources: ABACRole: Type: AWS::IAM::Role Properties: RoleName: GitHubActionsABACRole AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Federated: cognito-identity.amazonaws.com Action: sts:AssumeRoleWithWebIdentity Condition: StringEquals: cognito-identity.amazonaws.com:aud: !Ref IdentityPool Policies: - PolicyName: ABACDynamicAccess PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: "*" Resource: "*" Condition: StringEquals: aws:ResourceTag/Repository: "${aws:PrincipalTag/repository}" 4. GitHub Actions Workflow with OIDC → Cognito name: ABAC Deployment on: [push] permissions: id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest steps: - name: Configure AWS Credentials via Cognito uses: catnekaise/cognito-idpool-auth@v2 with: auth-flow: enhanced cognito-identity-pool-id: ${{ secrets.COGNITO_POOL_ID }} aws-region: us-west-2 set-in-environment: true - name: Deploy Resources run: | aws s3api create-bucket --bucket my-bucket \ --tagging "Repository=${{ github.repository }}" # ✅ Allowed: bucket tagged correctly aws s3 ls s3://my-bucket # ❌ Denied: bucket with incorrect tag aws s3 ls s3://other-bucket How It Works: Identity and Tags in Action GitHub OIDC Claims: { "repository": "org/my-repo", "environment": "prod", "job_workflow_ref": ".github/workflows/deploy.yml" } These map into: Cognito Principal Tags: repository, environment, workflow AWS Se

Apr 25, 2025 - 17:23
 0
Secretless CI/CD with GitHub Actions OIDC

Managing AWS credentials for CI/CD pipelines can quickly become complex and risky. Hardcoding AWS credentials or storing them as GitHub Secrets exposes your organization to credential leakage, auditability challenges, and significant security risks. Similarly, statically binding IAM roles to runners complicates scaling, limits flexibility, and creates management overhead — especially in multi-account environments.

In this tutorial, you'll learn how to securely manage AWS access from GitHub Actions using AWS Attribute-Based Access Control (ABAC), GitHub Actions OIDC tokens, and Amazon Cognito as an identity broker — all without static credentials or brittle IAM bindings. This model maps OIDC claims from your CI/CD workflows to a short-lived AWS STS token. This model can be extended to any service provider that you need to connect to from your CI/CD workflows, as long as the service provider is OIDC compliant and supports Identity Federation.

Why Avoid Hardcoded Credentials and Static IAM Bindings?

Security Risks: Hardcoded secrets can leak through logs, repos, or misconfigurations.
Auditability Issues: Static credentials obscure who performed actions and when.
Operational Bottlenecks: Static role-to-runner bindings make it hard to scale access across apps or environments.

If you’re scaling GitHub Actions across multiple teams, apps, or accounts — you need a better way. A least privileged access model along with dynamic infrastructure scaling.

Secure, Scalable Architecture Overview

This architecture connects GitHub Actions to AWS securely using temporary session credentials based on identity claims and session tags.

GitHub Actions → [OIDC Token] 
     ↓
Cognito Identity Pool → [Maps Claims → Principal Tags]
     ↓
AWS STS → [AssumeRoleWithWebIdentity → Session with Tags]
     ↓
AWS Resources (enforced via ABAC)

Step-by-Step Setup

1. Create an OIDC Identity Provider for GitHub

Resources:
  GitHubOIDCProvider:
    Type: AWS::IAM::OIDCProvider
    Properties:
      Url: https://token.actions.githubusercontent.com
      ClientIdList:
        - "sts.amazonaws.com"
      ThumbprintList:
        - "6938fd4d98bab03faadb97b34396831e3780aea1"

You can also configure this manually via the IAM Console → Identity Providers.

2. Configure Cognito Identity Pool

Map OIDC claims from GitHub into principal tags.

Resources:
  IdentityPool:
    Type: AWS::Cognito::IdentityPool
    Properties:
      IdentityPoolName: GitHubActionsABAC
      AllowClassicFlow: true
      OpenIdConnectProviderARNs:
        - !GetAtt GitHubOIDCProvider.Arn
      PrincipalTags:
        repository: "repo:repository"
        environment: "repo:environment"
        workflow: "repo:job_workflow_ref"

  IdentityPoolRoleAttachment:
    Type: AWS::Cognito::IdentityPoolRoleAttachment
    Properties:
      IdentityPoolId: !Ref IdentityPool
      Roles:
        authenticated: !GetAtt ABACRole.Arn

3. Define the ABAC IAM Role

This IAM role uses session tags for authorization.

Resources:
  ABACRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: GitHubActionsABACRole
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Federated: cognito-identity.amazonaws.com
            Action: sts:AssumeRoleWithWebIdentity
            Condition:
              StringEquals:
                cognito-identity.amazonaws.com:aud: !Ref IdentityPool
      Policies:
        - PolicyName: ABACDynamicAccess
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: "*"
                Resource: "*"
                Condition:
                  StringEquals:
                    aws:ResourceTag/Repository: "${aws:PrincipalTag/repository}"

4. GitHub Actions Workflow with OIDC → Cognito

name: ABAC Deployment
on: [push]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Configure AWS Credentials via Cognito
        uses: catnekaise/cognito-idpool-auth@v2
        with:
          auth-flow: enhanced
          cognito-identity-pool-id: ${{ secrets.COGNITO_POOL_ID }}
          aws-region: us-west-2
          set-in-environment: true

      - name: Deploy Resources
        run: |
          aws s3api create-bucket --bucket my-bucket \
            --tagging "Repository=${{ github.repository }}"

          # ✅ Allowed: bucket tagged correctly
          aws s3 ls s3://my-bucket

          # ❌ Denied: bucket with incorrect tag
          aws s3 ls s3://other-bucket

How It Works: Identity and Tags in Action

GitHub OIDC Claims:

{
  "repository": "org/my-repo",
  "environment": "prod",
  "job_workflow_ref": ".github/workflows/deploy.yml"
}

These map into:

Cognito Principal Tags: repository, environment, workflow
AWS Session Tags: Used in IAM policies to enforce access

Real-World Scenario: Multi-Account, Multi-App

ABAC lets you scale your platform securely:

Multi-account CI/CD: Assign different roles for dev/stage/prod accounts.
Shared runners: Let multiple apps use the same runners — securely and in isolation.
No over-permissioning: Runners don’t get ambient access to resources.

GitHub Actions
   ↓
Cognito Identity Pool
   ├─ Dev Role → Dev Resources
   └─ Prod Role → Prod Resources

Benefits Recap

  • No long-lived AWS credentials in GitHub
  • Least-privilege access per job, repo, or environment
  • Clean audit trail — every access mapped to identity
  • Flexible scaling for CI/CD across teams and cloud accounts.

Further Reading

AWS ABAC Overview
GitHub Actions OIDC
AWS Cognito Identity Pools

Secure your GitHub Actions pipelines the right way — with identity, not secrets.