【Terraform】 GitHub Actions から OIDC 認証によって AWS ECS にデプロイ

エキサイト株式会社の@mthiroshiです。

GitHub Actions から AWS ECS にコンテナアプリケーションをデプロイする際に、OIDC 認証を使用して AWS にアクセスする方法を試してみました。その内容について紹介します。

GitHub 公式ドキュメントは下記です。 docs.github.com

OpenID Connect(OIDC)とは

OpenID Connect(OIDC)は、OAuth 2.0 フレームワーク仕様に基づいた認証プロトコルです。 OIDC を利用することで、アプリケーションはユーザーを検証し、必要なユーザー情報を取得できます。

GitHub Actions から AWS ECS へデプロイする場合、AWS へのアクセスには認証が必要となります。 認証方法の一つとして、IAM ユーザーのアクセスキーとシークレットキーを用いる方法がありましたが、この方法はキーのセキュアな管理が必要であり、運用上の課題となっていました。

それに対して OIDC を用いる場合では、有効期間の短いクレデンシャルを AWS から直接要求するようにワークフローを構成できます。 OIDC では、アクセストークンと呼ばれる有効期限付きのトークンを発行するため、アクセスキーやシークレットキーのように永続的に管理する必要がなく、セキュリティリスクを低減することができます。

AWS IAM の ID プロバイダーに GtiHub OIDC プロバイダーを追加

GitHub の OIDC プロバイダー を AWS IAM の ID プロバイダに追加します。これによって、GitHubAWS アカウント間の信頼を確立します。

Terraform コードを紹介します。

data "tls_certificate" "github_actions_deploy" {
  url = "https://token.actions.githubusercontent.com/.well-known/openid-configuration"
}

resource "aws_iam_openid_connect_provider" "github_actions_deploy" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = [data.tls_certificate.github_actions_deploy.certificates[0].sha1_fingerprint]
}

aws_iam_openid_connect_provider リソースによって、IAM OIDC ID プロバイダーを作成できます。

下記ページを参考にして、パラメータの値を当てはめています。 docs.github.com

For the provider URL: Use https://token.actions.githubusercontent.com

For the "Audience": Use sts.amazonaws.com if you are using the official action.

thumbprint_list には、 OIDC ID プロバイダーのサーバー証明書サーバー証明書のサムプリントが求められるので、 tls_certificate data リソースによって取得し、設定します。

GitHub Actions 用の IAM ロールを作成

GitHub Actions から AWS にアクセスするための IAM ロールを作成します。 IAM ロールを作成し、下記の3種類の IAM ポリシーをアタッチします。

  • OIDC 認証用の信頼ポリシー
  • ECS デプロイ用の許可ポリシー(アプリケーション共通)
  • ECS デプロイ用の許可ポリシー(アプリケーション個別)

まず、 IAM ロール作成と、OIDC 認証の 信頼 ポリシー、ECS デプロイ用の許可ポリシー(アプリケーション共通)のアタッチについて、Terraform コードを示します。

data "aws_iam_policy_document" "github_actions_deploy_assume_policy" {
  statement {
    principals {
      type = "Federated"
      identifiers = [
        aws_iam_openid_connect_provider.github_actions_deploy.arn
      ]
    }

    actions = [
      "sts:AssumeRoleWithWebIdentity"
    ]

    condition {
      test     = "StringLike"
      variable = "token.actions.githubusercontent.com:sub"
      values   = ["repo:<GitHubオーガニゼーション>/<GitHubリポジトリ名>:*"]
    }
  }
}

resource "aws_iam_role" "github_actions_deploy" {
  name               = "github-actions-deploy"
  description        = "IAM Role for GitHub Actions Deploy ECS"
  assume_role_policy = data.aws_iam_policy_document.github_actions_deploy_assume_policy.json
}

data "aws_caller_identity" "self" {}

data "aws_iam_policy_document" "github_actions_deploy_policy" {
  statement {
    sid = "GetAuthorizationToken"
    actions = [
      "ecr:GetAuthorizationToken"
    ]
    resources = ["*"]
  }

  statement {
    sid = "RegisterTaskDefinition"
    actions = [
      "ecs:RegisterTaskDefinition"
    ]
    resources = ["*"]
  }

  statement {
    sid = "PassRole"
    actions = [
      "iam:PassRole"
    ]
    resources = [
      "arn:aws:iam::${data.aws_caller_identity.self.account_id}:role/ecsTaskExecutionRole"
    ]
    condition {
      test     = "StringLike"
      variable = "iam:PassedToService"
      values   = ["ecs-tasks.amazonaws.com"]
    }
  }
}

resource "aws_iam_policy" "github_actions_deploy" {
  name   = "github-actions-deploy"
  policy = data.aws_iam_policy_document.github_actions_deploy_policy.json
}

resource "aws_iam_role_policy_attachment" "role_policy_attachment" {
  role       = aws_iam_role.github_actions_deploy.name
  policy_arn = aws_iam_policy.github_actions_deploy.arn
}

data "aws_iam_policy_document" "github_actions_deploy_assume_policy" では、先ほどの OIDC IDプロバイダーを指定し、AssumeRoleWithWebIdentity アクションによって、一時的な認証情報を取得できるように信頼ポリシーを作成しています。 認証の条件に、 GitHub リポジトリの指定もできます。

そして、data "aws_iam_policy_document" "github_actions_deploy_policy" では、デプロイ用の許可ポリシー(アプリケーション共通)を設定しています。ECRのログイン、ECSのタスク定義登録などのポリシーです。

タスク実行ロール( ecsTaskExecutionRole ) に iam:PassRole をしています。タスク実行の際に、OIDC 認証のポリシーが必要となるためです。

次に、ECS デプロイ用の許可ポリシー(アプリケーション個別)の設定です。

data "aws_iam_policy_document" "github_actions_deploy" {
  statement {
    sid = "PushImageOnly"
    actions = [
      "ecr:BatchCheckLayerAvailability",
      "ecr:InitiateLayerUpload",
      "ecr:UploadLayerPart",
      "ecr:CompleteLayerUpload",
      "ecr:PutImage"
    ]
    resources = [
      "arn:aws:ecr:ap-northeast-1:<AWSアカウントID>:repository/<リポジトリ名>",
    ]
  }

  statement {
    sid = "UpdateService"
    actions = [
      "ecs:UpdateServicePrimaryTaskSet",
      "ecs:DescribeServices",
      "ecs:UpdateService"
    ]
    resources = [
      "arn:aws:ecs:ap-northeast-1:<AWSアカウントID>:service/<サービス名>"
    ]
  }

  statement {
    sid = "PassRole"
    actions = [
      "iam:PassRole"
    ]
    resources = [
      "arn:aws:iam::<AWSアカウントID>:role/<タスクロール>"
    ]
  }
}

resource "aws_iam_policy" "github_actions_deploy" {
  name   = "github-actions-deploy-policy"
  policy = data.aws_iam_policy_document.github_actions_deploy.json
}

resource "aws_iam_role_policy_attachment" "role_policy_attachment" {
  role       = "arn:aws:iam::<AWSアカウントID>:role/<タスクロール>",
  policy_arn = aws_iam_policy.github_actions_deploy.arn
}

ecr:PutImageecs:UpdateSerivice などのアクションでは、それを実行できるアプリケーションを制限しようと考えました。そのように設定するためには、ECS サービスのarnを動的に参照する必要があったため、Terraformを分ける運用としました。

ECS タスクロールにも OIDC 認証を渡せるように iam:PassRole を設定します。

GitHub Actions の設定

ECS にコンテナをデプロイする GitHub Actions の yaml のサンプルコードを示します。

簡単に、チェックアウトから、ECRログイン、ビルド、タスク定義のデプロイまでを示しています。

name: Deploy ECS

on:
  workflow_dispatch:

permissions:  
  contents: read
  id-token: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::<AWSアカウントID>:role/github-actions-deploy
          aws-region: ap-northeast-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2
     
      # ビルド 省略

      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: task-definition.json
          cluster: "sample_ecs_cluster"
          service: "sample_ecs_service"
          wait-for-service-stability: true

今回の趣旨のポイントを説明します。

まず、 permissionsid-token: write が必要になります。これによって、GitHubの OIDC プロバイダーから JWT を要求できます。

そして、aws-actions/configure-aws-credentials@v4 action の role-to-assume に作成した IAM ロールの ARN を指定します。これで AWS へアクセスが可能になり、以降のECR ログイン等の処理が実行できます。

最後に

GitHub Actions から OIDC 認証によって AWS へアクセスする方法について、まとめてみました。 IAM ロール、ポリシー周りは、理解が浅くてハマった部分でした。ここは改めて整理しておきたいと思います。 参考になれば幸いです。

採用情報

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。

募集職種一覧はこちらになります!(カジュアル面談もぜひ!) www.wantedly.com

参考記事

アマゾン ウェブ サービスでの OpenID Connect の構成 - GitHub Enterprise Cloud Docs

GitHub ActionsでOIDCによるAWS認証をTerraformで実装する

GitHub ActionsからECSとECRへのCI/CDを最小権限で実行したい | DevelopersIO