Introduction

Every DevOps engineer knows the importance of secure, streamlined workflows. A while ago, I found myself managing an array of AWS resources across multiple accounts. My goal was to automate deployments and other tasks using GitHub Actions. The catch? I wanted to avoid hardcoding AWS secret keys in my repositories. The solution? Connecting GitHub Actions directly to an AWS IAM Identity Provider (Idp). This method not only enhanced security but also simplified the management of credentials.

In this blog post, I’ll walk you through how to connect an AWS IAM Idp to GitHub Actions, allowing you to securely assume roles without needing to store long-lived AWS access keys.

Why Use AWS Idp with GitHub Actions?

Traditionally, many developers have used AWS access keys (Access Key ID and Secret Access Key) to authenticate GitHub Actions workflows with AWS. However, this approach has several downsides:

  • Security Risks: Storing long-lived access keys in your repository or even in GitHub Secrets increases the risk of unauthorized access, especially if those keys are compromised.
  • Management Overhead: Rotating access keys regularly is a good practice but adds administrative overhead.
  • Scalability: Managing access keys across multiple repositories or teams can become cumbersome and error-prone.

While using AWS credentials stored in GitHub Secrets might seem simpler initially, the security risks associated with it are significant. To mitigate these risks, I strongly recommend using IAM Idp as the preferred method for accessing AWS from GitHub Actions. This approach, which leverages IAM roles and OIDC (OpenID Connect) Idp, allows GitHub Actions to securely request temporary credentials from AWS, eliminating the need for static access keys and providing a more secure and scalable solution for managing credentials and controlling access to AWS resources.

Setting Up the IAM Idp in AWS

Before you can connect GitHub Actions to AWS using an Idp, you need to set up the Idp in AWS. This setup involves creating an IAM Identity Provider in AWS, establishing trust relationships, and scoping the access to your specific GitHub organization.

Create the IAM Identity Provider in AWS

First, you need to create an Idp for GitHub in your AWS account. This allows AWS to trust GitHub’s OIDC tokens and issue temporary credentials based on them.

  1. Navigate to the IAM Console:
    • Go to the AWS Management Console and navigate to the IAM (Identity and Access Management) service.
  2. Create an Identity Provider:
    • In the IAM dashboard, select "Identity Providers" under "Access management" in the sidebar.
    • Click the "Add provider" button.
  3. Configure the Provider:
    • Provider type: Choose "OpenID Connect".
    • Provider URL: Enter https://token.actions.githubusercontent.com.
    • Audience: You can enter sts.amazonaws.com. This is the audience value that GitHub will include in the JWT token.
  4. Create the Provider:
    • Review the settings and click "Add provider" to create the identity provider.

Establish a Trust Relationship

After creating the Idp, you need to establish a trust relationship with a role in AWS that GitHub Actions can assume. This trust relationship defines who can assume the role and under what conditions.

  1. Create or Choose an IAM Role:
    • If you don’t have a specific IAM role, create a new role. Go to the IAM dashboard, select "Roles" and click "Create role".
    • Choose "Custom trust policy" or "Web identity" as the trusted entity type.
    • For "Identity provider", select the provider you just created.
  2. Define the Trust Policy:
    • Use the following trust policy as a starting point. This policy allows GitHub Actions to assume the role, but only if the request comes from your specific GitHub organization and repository.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:sub": "repo:<GITHUB_ORG_NAME>/<REPOSITORY_NAME>:ref:refs/heads/main",
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
    • Replace <AWS_ACCOUNT_ID> with your AWS Account ID.
    • Replace <GITHUB_ORG_NAME> with your GitHub organization name.
    • Replace <REPOSITORY_NAME> with your repository name.
    • The "ref:refs/heads/main" condition ensures that only workflows triggered on the main branch can assume the role.
  1. Add the Trust Policy to Your Role:
    • If you are creating a new role, paste the policy into the "Custom trust policy" section.
    • If you are modifying an existing role, you can update the trust relationship by editing the "Trust relationships" tab of the role.
  1. Optional: Scope the Role to Your GitHub Organization
    • To limit the role to your specific GitHub organization, modify the sub condition in the trust policy. The sub condition specifies who can assume the role based on the GitHub repository.
"Condition": {
  "StringLike": {
    "token.actions.githubusercontent.com:sub": "repo:<GITHUB_ORG_NAME>/*"
  },
  "StringEquals": {
    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
  }
}
    • This condition allows any repository within the <GITHUB_ORG_NAME> organization to assume the role, as long as the other conditions (like the audience) are met.

Setting Up GitHub Actions to Connect to AWS

To demonstrate this setup, we will create a GitHub Actions pipeline that connects to AWS using an IAM role and then performs some tasks, such as retrieving the caller identity. Below is a high-level overview of the pipeline:

name: Connect to an AWS role from a GitHub repository

# Controls when the action will run. Invokes the workflow on push events but only for the main branch
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  AWS_REGION : "us-east-1" #Change to reflect your Region

# Permission can be added at job level or workflow level    
permissions:
      id-token: write   # This is required for requesting the JWT
      contents: read    # This is required for actions/checkout
jobs:
  ConnectToAWS:
    runs-on: ubuntu-latest
    steps:
      - name: Git clone the repository
        uses: actions/checkout@v4
      - name: configure aws credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::<AWS_ACCOUNT_ID>:role/<ROLE_NAME> #change to reflect your IAM role’s ARN
          aws-region: ${{ env.AWS_REGION }}
      # Hello from AWS: WhoAmI
      - name: Sts GetCallerIdentity
        run: |
          aws sts get-caller-identity

Breaking Down the Pipeline

1. Setting Up Permissions

The permissions section grants the required permissions for the workflow. The id-token: write permission is crucial because it allows GitHub Actions to request a JWT (JSON Web Token) from GitHub's OIDC provider. This token is then exchanged for temporary AWS credentials.

2. Checking Out the Repository

The first step in the job is to check out the repository using the actions/checkout@v4 action. This action ensures that the workflow has access to the code in your repository.

3. Configuring AWS Credentials

The aws-actions/configure-aws-credentials@v4 action is the heart of the setup. This action uses the JWT from GitHub to assume an IAM role in AWS, configured with the role-to-assume parameter. The role must have trust relationships set up to allow GitHub's OIDC provider to assume it.

4. Verifying the Connection

The final step in the example pipeline is to verify the connection by running the aws sts get-caller-identity command. This command retrieves details about the IAM identity that was used to assume the role, confirming that the pipeline is authenticated with AWS. The result will look like this:

{
    "UserId": "AROARJI3OP5IHJAPMQTGK:GitHubActions",
    "Account": "1234567890",
    "Arn": "arn:aws:sts::1234567890:assumed-role/RoleName/GitHubActions"
}

Why This Approach Is Better

Using an IAM Idp with GitHub Actions offers several benefits:

  • Enhanced Security: By using temporary credentials and avoiding static access keys, the risk of credential leaks is significantly reduced.
  • Ease of Use: Once set up, the process of assuming roles is seamless, requiring minimal changes to your GitHub Actions workflows.
  • Scalability: This approach scales well across multiple repositories and teams, as it centralizes credential management within AWS IAM.
Share this post