Many times when we need to connect to AWS through GitHub Actions, the first thing that comes to mind is to take the access credentials of an IAM user that we have created, and use them as environment variables in our workflow file, in order to authenticate a user in AWS. But this method is not the most secure, as we need to hand over our AWS credentials.
Luckily for us, there is another method that we can use to authenticate to AWS through GitHub Actions without using our credentials. You can assume an IAM role.
Let’s get started!
Identity provider
First, add GitHub as OpenID provider to IAM Identity providers. This connects AWS and GitHub, so they can exchange tokens.
Provider URL: https://token.actions.githubusercontent.com
Audience: sts.amazonaws.com
IAM role
Instead of a user, you have to create a role with a trust relationship. It’s a relationship between the role and the added GitHub identity provider.
Press Next to add permissions.
Once the IAM Role is created, the last step is changing the trust relationship condition to restrict usage of the role.
Click on “Edit trust relationship” to start editing.
And add:
..
“Condition”: {
“StringLike”: { “token.actions.githubusercontent.com:sub”:”repo:organization/repository:*”
}
}
Update the workflow file
Finally, we can update the workflow file. This is a simplified version of a workflow file:
name: Deploy
on:
push:
jobs:
deploy:
name: Deploy
runs-on: ubuntu-18.04
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v2
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: arn:aws:iam::111111111111:role/deploy-xyz.tech
aws-region: eu-central-1
- name: Build
What changed?
The secrets containing AWS credentials have been removed:
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
And we replace it with this:
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v2
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: arn:aws:iam::111111111111:role/deploy-xyz.tech
aws-region: eu-central-1
Using Terraform
The configuration via Terraform is quite simple and it does not require any advanced knowledge.
Let’s start creating our OpenID Connect 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 it’s generated based on the certificate’s ssl key of the openid from Github. This value is based on the url, so this is static and you can just copy & paste without any hassle.
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:xyz1/*:*”,
“repo:xyz2/*:*”
]
}
}
In the example above, any repository from xyz1 or xyz2 have permissions sts:AssumeRoleWithWebIdentity therefore they can assume a role that we will create next.
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
}
After that, another policy document must be created, but this time it will contain permissions for the Github Actions.
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”]
}
}
Notice that in the example above we are using ECR but nothing blocks you from give any other permission to other AWS services.
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 = “xyz/repo”
image_tag_mutability = “IMMUTABLE”
image_scanning_configuration {
scan_on_push = true
}
tag = {
“permit-github-action” = true
}
}
Finally we update the workflow file in the same way that we did previously in the steps without Terraform.
All done!
And that’s it. Now you can remove the user! Or disable the access key id first, just to be sure.
Thanks for reading!
Teracloud is an AWS Advanced Consulting Partner specialized in Cloud Computing (AWS, Azure, GCP) best practices. We help SaaS of different industries around the world scale their business safely.
Contact our team at info@teracloud.io for an assessment. Let us handle migration, automation, deployment, performance, cost optimization and machine learning to make your business grow.
We make the cloud easy for you!