Updating AWS ECR Image Using OIDC with GitHub Actions
In the modern software development cycle, CI/CD (Continuous Integration/Continuous Deployment) play an integral part. One crucial aspect of this process is managing the deployment of Docker images to cloud-based repositories, like Amazon Elastic Container Registry (ECR). This article will be detailing the process of pushing an ECR image using GitHub Actions and the OpenID Connect (OIDC) protocol.
What is OIDC
GitHub Actions workflows are used to access cloud providers such as AWS, Google Cloud Platform (GCP), or Azure to deploy software or utilize cloud services. To facilitate secure communication between these different platforms, OpenID Connect (OIDC) is used.
OIDC is an identity layer that verifies the end-user's identity based on authentication performed by an authorization server. This allows the issuance of credentials during workflow execution, which can be used for authentication during deployment to the cloud. These credentials are temporary and reduce the risk of misuse if leaked, compared to using permanent access keys.
As of September 2022, the cloud providers supporting OIDC include:
- AWS
- Azure
- GCP
- HashiCorp Vault
Source Code
Directory Structure
Our project's directory structure is organized as follows:
.
├── .github/workflows/deploy-ecr.yml
├── Dockerfile
└── terraform
├── variables.tf
├── iam.tf
└── ecr.tf
Terraform
The following code defines OIDC providers, roles, and ECR repositories.
variable "env" {
default = "dev"
}
variable "github_provider" {
default = "arn:aws:iam::<AWS_ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
}
variable "aws_account_id" {
default = "123456789012"
}
variable "github_org" {
default = "mycompany-inc"
}
variable "github_repo" {
default = "my-repo"
}
resource "aws_iam_openid_connect_provider" "main" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
resource "aws_iam_role" "github_actions" {
name = "${var.env}-github-actions"
assume_role_policy = jsonencode(
{
"Version" = "2012-10-17"
"Statement" = [
{
"Principal" : {
"Federated" : "arn:aws:iam::${var.aws_account_id}:oidc-provider/token.actions.githubusercontent.com"
},
"Action" : [
"sts:AssumeRoleWithWebIdentity",
],
"Effect" = "Allow",
"Condition" : {
"StringLike" : {
"token.actions.githubusercontent.com:aud" : "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub" : "repo:${var.github_org}/${var.github_repo}:*"
}
},
}
]
}
)
}
resource "aws_iam_policy" "github_actions_policy" {
name = "${var.env}-github-actions-policy"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "ImageUploader",
"Action" : [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:GetAuthorizationToken",
],
"Effect" = "Allow",
"Resource" = "*"
}
],
})
}
resource "aws_iam_role_policy_attachment" "github_actions_ecr_policy_attachment" {
policy_arn = aws_iam_policy.github_actions_policy.arn
role = aws_iam_role.github_actions.id
}
resource "aws_iam_role_policy_attachment" "github_actions_ecs_full_access" {
policy_arn = "arn:aws:iam::aws:policy/AmazonECS_FullAccess"
role = aws_iam_role.github_actions.id
}
resource "aws_ecr_repository" "main" {
name = "my-ecr-repo"
}
Github Actions
Register the following secret from Setting
> New repository secret
on GitHub.
AWS_GA_ROLE_ARN
(ARN ofaws_iam_role.github_actions
defined in Terraform)AWS_ECR_REPO_NAME
(ECR repository name defined in Terraform)
And write the following YML file in .github/workflows/deploy.yml
in the repository.
name: Deploy ECR Image
on:
push:
branches:
- develop
permissions:
id-token: write
contents: read
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: ap-northeast-1
role-to-assume: ${{ secrets.AWS_GA_ROLE_ARN }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build and push Docker image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPO_NAME }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY . --no-cache
docker push $ECR_REGISTRY/$ECR_REPOSITORY
References