2022-09-05

Updating AWS ECR Image Using OIDC with GitHub Actions

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.

variables.tf
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"
}
iam.tf
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
}
ecr.tf
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 of aws_iam_role.github_actions defined in Terraform)
  • AWS_ECR_REPO_NAME (ECR repository name defined in Terraform)

Secret setting

And write the following YML file in .github/workflows/deploy.yml in the repository.

.github/workflows/deploy-ecr.yml
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

https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect
https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services
https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-push.html
https://github.blog/changelog/2022-01-13-github-actions-update-on-oidc-based-deployments-to-aws/

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!