NOTE: For Portuguese readers: you can find a translated version here.
GitHub recently launched a new feature to authenticate via oidc on AWS from the actions workflows, giving us the chance to finally get rid of the process of managing a whole user specific for that interaction.
Basically, the auth now happens using OIDC and the only thing you need for that is to set up a role on AWS side and pass that info in your workflow.
In this article we will setup everything needed on AWS using Terraform, and of course, we will see how it works.
Terraform
The configuration via Terraform is quite simple and it does not require any advanced knowledge.
Let's start creating our oidc provider:
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
Both information url and client_id_list are given by GitHub in their documentation that you can read it here
The thumbprint_list was more tricky to understand since it's generated based on the certificate's ssl key of the openid from Github. What matters here is: this value is based on the url, so this is static and you can just copy & paste without any hassle.
In case you wanna go deeper in this topic, read a bit Obtaining the thumbprint for an OpenID IdP and also this Changelog.
The next step is to create our policy document, setting permission to our repositories do assume role:
data "aws_iam_policy_document" "github_actions_assume_role" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.github.arn]
}
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:aud"
values = ["sts.amazonaws.com"]
}
condition {
test = "StringLike"
variable = "token.actions.githubusercontent.com:sub"
values = [
"repo:org1/*:*",
"repo:org2/*:*"
]
}
}
In the example above, any repository from org1 or org2 have permissions sts:AssumeRoleWithWebIdentity therefore they can assume a role that we will create soon.
It's possible to restrict this permission to allow only certains repositories (or even specific branches), in case you wanna try that you just need to change your condition block to:
condition {
test = "StringLike"
variable = "token.actions.githubusercontent.com:sub"
values = [
"repo:org/my-repo:*",
]
}
Another important thing to notice here is the value of token.actions.githubusercontent.com:aud
which I'm using as sts.amazonaws.com
and not as specified on Github Documentation
Following our tutorial, we now need to finally create our role and associate it to the policy document previously created:
resource "aws_iam_role" "github_actions" {
name = "github-actions"
assume_role_policy = data.aws_iam_policy_document.github_actions_assume_role.json
}
Now we need to create another policy document, but this time it will contain permissions for the Github Actions.
In our case, we will have permission to do some ECR operations on AWS, respecting the only rule that our registry must have a Tag permit-github-action=true
data "aws_iam_policy_document" "github_actions" {
statement {
actions = [
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:GetDownloadUrlForLayer",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart",
]
resources = ["*"]
condition {
test = "StringEquals"
variable = "aws:ResourceTag/permit-github-action"
values = ["true"]
}
}
Please notice that in our example we are using ECR but nothing blocks you from give any other permission to other AWS services: S3, SQS, etc.
And finally, we need to create our policy based on the policy document we created earlier and attach it to the role:
resource "aws_iam_policy" "github_actions" {
name = "github-actions"
description = "Grant Github Actions the ability to push to ECR"
policy = data.aws_iam_policy_document.github_actions.json
}
resource "aws_iam_role_policy_attachment" "github_actions" {
role = aws_iam_role.github_actions.name
policy_arn = aws_iam_policy.github_actions.arn
}
As a last step for the Terraform part, we need to create our registry and add a Tag to it:
resource "aws_ecr_repository" "repo" {
name = "meu/repositorio"
image_tag_mutability = "IMMUTABLE"
image_scanning_configuration {
scan_on_push = true
}
tag = {
"permit-github-action" = true
}
}
Github Actions
Basically the configuration on the Github Actions side is quite simple and it does not require too much effort on your workflows if you are already authenticating using access key and secret keys.
The first thing you need to setup, according to Github Documentation is the permissions:
permissions:
id-token: write
contents: read
And after, on the step you use to configure the credentials, you need to inform the role we created with terraform.
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@master
with:
role-to-assume: arn:aws:iam::XXXXXXXXXXXX:role/github-actions
aws-region: eu-west-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
And here you can check the final content of the YAML file.
name: Continuous Delivery
on: [push, pull_request]
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@master
with:
role-to-assume: arn:aws:iam::XXXXXXXXXXXX:role/github-actions
aws-region: eu-west-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Checkout
uses: actions/checkout@v2
- name: Build, tag, and push image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: my/repo
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
I hope you enjoyed this article! :)
Top comments (5)
thank you....this was really helpful...
one change that was recently published by Github was related to the OIDC Thumbprint - github.blog/changelog/2022-01-13-g...
Thank you for pointing this out!
Great article! Thanks!
Really made things clear.
Got it working in no time.
Great article. Followed this pretty closely to get up and running really quickly. You may want to add the following action:
ecr:GetAuthorizationToken
Without it I wasn't able to run my basic action. It showed up in the debug console.
Thanks for sharing, when creating the registry, you need to use
tags
insteadtag
, example: