I've been tinkering with GitHub actions over the last few months, often times with some kind of AWS interaction in my workflow. As it turns out, there are simple and less simple methods of authenticating your workflows with AWS. Let's explore how to use GitHub's OIDC provider to free yourself from hardcoded AWS credentials.
Hardcoded access keys
Lets be real, we've all hardcoded access keys before. For beginners and people who are just looking to get something running, hardcoded credentials are great, it's the most straightforward way to authenticate your workflow with AWS.
Here's a typical example:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
However, this does not align with best practices, duplicating and storing long lived credentials for your AWS account is not the most secure way to authenticate your workflows.
Enter OpenID Connect (OIDC)
OIDC, or OpenID Connect, is an authentication protocol that lets a third party (in our case, AWS) verify a user's identity through a JSON Web Token (JWT). With OIDC, authentication is outsourced to the OIDC provider. Once authenticated, the user can obtain a JWT from the identity provider, using it to authenticate with the third party.
In our scenario, GitHub acts as an OIDC provider for workflows. This means our workflow can request a JWT from GitHub and then use it to assume an AWS role by obtaining temporary credentials from AWS.
How to use GitHub's OIDC provider in your workflows
Let's walk through a simple example that builds a Docker image and pushes it to ECR using GitHub's OIDC provider. If you want to follow along, I've prepared a NodeJS application and Docker file in this following repository:
https://github.com/Nathan-Bridgewater/GitHub-AWS-OIDC.git
1. Create an identity provider in AWS
Firstly, we add GitHub as an identity provider. This configures AWS to accept OIDC tokens from a workflow and return temporary access credentials.
aws create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 1b511abead59c6ce207077c0bf0e0043b1382612 \
2. Create the trust policy
Craft a JSON file containing a trust policy for an AWS role, specifying which GitHub workflows can assume our role.
trust-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRoleWithWebIdentity",
"Principal": {
"Federated": "arn:aws:iam::<ACCOUNT NUM>:oidc-provider/token.actions.githubusercontent.com"
},
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": [
"sts.amazonaws.com"
]
},
"StringLike": {
"token.actions.githubusercontent.com:sub": [
"repo:Nathan-Bridgewater/GitHub-AWS-OIDC:*"
]
}
}
}
]
}
3. Create the role
We need to establish a role for our federated workflow to assume, and attach a policy allowing our workflow to push images to ECR.
aws iam create-role \
--role-name github-workflow-role \
--assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy \
--role-name github-workflow-role \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
4. Create an ECR repository
Set up an ECR repository to push Docker images.
aws ecr create-repository \
--repository-name nodejs-images
5. Create the GitHub workflow
push-docker-actions.yaml
name: push-docker-ecr-actions
run-name: ${{ github.actor }} is running the push-docker-ecr workflow
on:
push:
branches:
- 'main'
permissions:
id-token: write
contents: read
jobs:
build-image-push-to-ecr:
name: Build docker image and push to ecr
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::<ACCOUNT NUM>:role/github-workflow-role
aws-region: us-east-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag and push image to ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: nodejs-images
IMAGE_TAG: latest
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
6. Test it out
Once added, your workflows should now authenticate with AWS without hardcoded credentials. Freedom awaits!
Top comments (0)