AWS

GitHub ActionsをOIDCでAWS認証してTerraformを実行する

CI/CDサービスとしてGitHub Actionsが有名です。

最近はOIDC認証が利用できると聞いたので試してみたい!
どうせならTerraformも実行させてみたい!

この記事では、GitHub ActionsOIDCAWS認証を行い、Terraformを実行する方法を紹介します。

対象読者
  • Terraformは使えるけど、GitHub Actionsなんも分からん…
  • でもGitHub ActionsTerraformを実行するようにしたい!
  • OIDCGitHub ActionsからAWSの認証をしたい!
この記事でやること
  • GitHub ActionsでTerraformを実行する
  • AWSの認証をOIDCで行う

前提条件

本記事で使用するリポジトリ

この記事で使用例として登場するリポジトリは筆者の以下のリポジトリです。

github.com/akashikaikyouohasi/GitHubActionsTestByOIDC

ワークフローの実行タイミング

今回はmainブランチへのプルリクエストがマージされた際に実行するようにしています。

実行環境

以下の記事で作成したCloud9のTerraformの実行環境を使用しています。

Cloud9でTerraform
Cloud9にTerraform環境を作ってみる どこでも同じ環境が使えるAWS Cloud9は便利です。しかし、Cloud9そのものの設定は必要です。 この記事では、Cloud...

GitHub Actionsとは

簡単に解説すると、GitHubCI/CDを自動化できる機能です。

公式のトップページの紹介文は以下の通り。

GitHub Actionsを使用すると、ワールドクラスのCI / CDですべてのソフトウェアワークフローを簡単に自動化できます。 GitHubから直接コードをビルド、テスト、デプロイでき、コードレビュー、ブランチ管理、問題のトリアージを希望どおりに機能させます。

https://github.co.jp/features/actions

CI継続的インテグレーションCD継続的デリバリーのことです。
ソフトウェアの変更を検知し、デプロイして自動でテストしてデプロイすることができます。

今回の記事ではTerraformを自動で実行できるようにします。
例えば、プルリクでレビュー用の環境を作ってデプロイし、レビューが完了してマージしたら削除みたいなことができます。

CI/CDサービスとして有名なのものにCircleCIがありますが、GitHubにリポジトリを持っている場合はGitHub Actionsの方が簡単に早く始められると思います!

手動でしなくてもいいことは積極的に機械に任せるべきなので、どんどん利用していきたいですね!

ワークフロー

GitHub Actionsを実行するためのワークフローを準備します。
ワークフロー内でAWSに接続する必要があるため、まずはAWSの認証ができるように準備します。
その後、Terraformの実行方法を準備します。

以前のやり方であれば、AWSのIAMユーザーのアクセスキーIDとシークレットアクセスキーを利用して認証していました。
この方法だとアクセスキーIDとシークレットアクセスキーが漏れた場合のセキュリティリスクが大きいです。

そのため、今回はGitHub Actionsで利用可能になったOIDC(OpenID Connect)を利用します。
OIDCを利用するとIDとキーをGitHub側で持つ必要がなくなります。
特に、AWS側で指定したGitHubのリポジトリだけを許可する設定を行うため、認証情報が漏れてもリスクが最小化されます!
(トークンのやり取りになるので、前任者にユーザー・パスワードを覚えられて外部から意図せず実行されることはないですね)

  • OIDCの説明については他の詳しい方の記事を見てみてください
  • 認可と認証の違いについてから入るのがいいと思いますよー

基本的にはGitHub Docsを参考に実施していきます

OIDC認証準備

IDプロバイダ作成

まずは、AWSでIDプロバイダを作成します。

  • AWSのサービス一覧からIAMを選択
  • アクセス管理の一覧からIDプロバイダを選択して、プロバイダを追加を選択
  • プロバイダのタイプOpenID Connectを選択して、残りは以下のように設定
  • その後、サムプリントを取得を選択して、問題なければプロバイダを追加を選択
項目設定内容
プロバイダの URLhttps://token.actions.githubusercontent.com
対象者sts.amazonaws.com
  • GitHubのプロバイダが作成できればOK!

IAMロールの作成

続いて、作成したIDプロバイダから利用できるIAMロールを作成します。

  • アクセスからロールを選択して、ロールを作成を選択
  • 信頼されたエンティティの種類を選択にてウェブIDを選択
  • IDプロバイダーは先ほど作成したGitHubのtoken.actions.githubusercontent.com:audを選択、Audiencesta.amazonaws.comを選択
  • 次のステップ:アクセス権限を選択
  • ポリシーのフィルタにAdministratorAccessと入力してAdministratorAccessポリシーを検索します
  • AdministratorAccessを選択して、次のステップ:タグを選択
  • 今回は操作を簡単にするために一番強いアクセス権限を付与しています
  • 本来は必要最低限の権限に絞って付与するものです!!!
  • タグはなしで、そのまま次のステップ:確認を選択。
  • 最後にロール名を付けます
    今回はGitHubActionsTestByOIDCとしますが、お好きな名前でも構いません
  • ロール名を設定したらロールの作成
  • ロール一覧画面に戻るので、検索バーGitHubActionsTestByOIDCと入力してロールが作成できていることを確認します。

IAMロールの信頼関係調整

作成したままではGitHub Actionsの認証で使用できません。
そのため、信頼関係を更新します。

  • 先ほど作成したIAMロールを選択します
  • 信頼関係タブに移動して、信頼関係の編集を選択します
  • 以下のようにポリシードキュメントを編集します
  • 11行目のCondition内をStringLikeにして、12行目をtoken.actions.githubusercontent.com:subに変更
  • 値はrepo:{GitHubのユーザー名}/{リポジトリ名}:*にします
  • 今回の値は、特定のリポジトリ内のすべてのブランチとイベントから実行を許可します
  • 特定のブランチやイベントのみの許可にすることも可能です 参考

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::{アカウントID}:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:akashikaikyouohasi/GitHubActionsTestByOIDC:*"
        }
      }
    }
  ]
}
  • Conditionは絶対に設定してください!
  • 設定しないとリポジトリの制限がかからなくなり、悪意を持ったっ人がIAMロールを使用できてしまいます!
  • 編集したら、信頼ポリシーの更新を選択
  • 問題なく更新出来たら、以下のように値が更新されています

これでAWS側の準備は完了です!

リポジトリの初期化

今回は、空のリポジトリの状態から開始するため初期化を行います。
なお、実行環境は以前にCloud9で作成したTerrafromが実行可能な環境を使用します。

Cloud9でTerraform
Cloud9にTerraform環境を作ってみる どこでも同じ環境が使えるAWS Cloud9は便利です。しかし、Cloud9そのものの設定は必要です。 この記事では、Cloud...

リポジトリの初期化

  • 実行環境にログインします
  • ローカルのリポジトリを作成して初期化と1stコミットを作成します
$ mkdir GitHubActionsTestByOIDC
$ cd GitHubActionsTestByOIDC/
$ git init
$ echo "# GitHubActionsTestByOIDC" >> README.md
$ git add README.md
$ git commit -m "first commit"
$ git branch -M main
$ git remote add origin git@github.com:akashikaikyouohasi/GitHubActionsTestByOIDC.git
  • 今のままではGitHubにアクセスできません
  • まずは、実行環境内でSSHキーを生成します
$ ssh-keygen
(質問事項は全てEnterでも構いません)
$ ls ~/.ssh/id_rsa.pub 
/home/ec2-user/.ssh/id_rsa.pub
  • GitHubのリポジトリのSettingsタブに移動します
  • SecurityからDeploy keysを選択し、キー登録画面に行きます
  • Titleは適当につけて、Keyには先ほど作成したキー(id_rsa.pub)の内容を貼り付けて、Allow write accessには忘れずチェックをつけること
  • Add keyを選択
  • 作成されればOKです!
  • ローカルの内容をGitHubのリモートリポジトリにプッシュします
$ git push -u origin main
  • リポジトリの画面で、READMEの内容が表示されればOKです!

ワークフロー作成

ここからはGitHub Actionsのワークフローを作成していきます。

  • GitHub Actionsでは.github/workflows/配下のディレクトリから設定を読み込むため、ディレクトリを作成
  • 空の設定ファイルterraform.ymlを作成します
$ mkdir -p .github/workflows/
$ cd .github/workflows/
$ touch terraform.yml
$ ls terraform.yml 
terraform.yml
  • terraform.ymlを以下のように更新します
    処理の説明は後ほど行います
name: terraform apply test by OIDC

on: 
  pull_request:
    branches:
      - main
    types: [closed]

permissions:
  id-token: write
  contents: read # actions/checkout のために必要

jobs:
  get-caller-identity:
    name: OIDC test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Configure AWS credentials from test account
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1
      
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v1
        with:
          terraform_version: 1.1.4

      - name: Terraform fmt
        id: fmt
        working-directory: ./terraform
        run: terraform fmt -check
        continue-on-error: true

      - name: Terraform Init
        id: init
        working-directory: ./terraform
        run: terraform init

      - name: Terraform Validate
        id: validate
        working-directory: ./terraform
        run: terraform validate -no-color

      - name: Terraform Plan
        id: plan
        working-directory: ./terraform
        run: terraform plan -no-color
        continue-on-error: true

      - name: Terraform Apply
        id: apply
        working-directory: ./terraform
        run: terraform apply -auto-approve
  • 編集が完了したら、コミットしてプッシュ
$ git add .
$ git commit -m "OIDCを利用してTerraformを実行するワークフローを追加"
$ git push 

先ほど作成したterraform.ymlの説明をしていきます。

各ワークフロー共通部分

terraform.ymlのうち、以下はどのワークフローでも利用する箇所です。

name: terraform apply test by OIDC

on: 
  pull_request: # プルリクエストで
    branches: # ブランチが
      - main # mainに対して
    types: [closed] # Closeされた時に実行

nameはワークフローの名前です。
適当でいいです

onはトリガー定義です。
いつGitHub Actionsでワークフローを実行するかを定義します。
今回の場合は、mainブランチへのPull Requestclose(マージされたのと同義)されたタイミングで実行するようになっています。

OIDC認証箇所

terraform.ymlのうち、以下でOIDC認証をおこなっています!

permissions:
  id-token: write # OIDCを利用する際に必須
  contents: read # actions/checkout のために必要

jobs:
  get-caller-identity: # ただの名前
    name: OIDC test # このJobの名前。ログに出ます
    runs-on: ubuntu-latest # 使用する環境。
    steps:
      - name: Checkout
        uses: actions/checkout@v2 # リポジトリをチェックアウトして取得します

      - name: Configure AWS credentials from test account
        uses: aws-actions/configure-aws-credentials@v1 # GitHubのOIDCプロバイダーからJWTを受け取り、AWSにアクセストークンを要求します
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }} # 作成したIAMロール。ただし、リポジトリのsecretsに設定しています。
          aws-region: ap-northeast-1 # 操作対象のリージョンを設定

permissionsはOIDCを使用するのに必要なid-token: writeと、actions/checkout@v2を利用する際に必要なcontens: readを設定しています。
公式ドキュメントの通りです。

jobsでは、リポジトリをチェックアウトした後、GitHubのOIDCプロバイダーからJWT(JSON Web Token)を受け取り、AWSにアクセストークンを要求します。
このシーケンスは公式の図この記事がわかりやすいです。

role-to-assumeでは上記で作成したIAMロールのARN(Amazon Resource Name)を指定しますが、アカウントIDが含まれるため一応secretsに登録しています。

IAMロールのARNのsecrets登録

  • リポジトリのSettingsからSecretsActiosを選択します
  • New Repository secretsを選択して、secretsを作成していきます
  • NameAWS_ROLE_ARNValueには上記で作成したIAMロールのARN(arn:aws:iam::{アカウントID}:role/GitHubActionsTestByOIDC)を設定します
  • 一覧に表示されればOKです!

これでGitHub Actionsのワークフローからsecretsが利用できます。

Terraform実行箇所

terraform.ymlのうち、以下でTerrafromを実行しています!

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v1 # Terraformのインストール
        with:
          terraform_version: 1.1.4 # Terraformのバージョン指定

      - name: Terraform fmt
        id: fmt
        working-directory: ./terraform # terraformディレクトリ内で実行
        run: terraform fmt -check # fmtをチェックし、フォーマット漏れがあれば失敗となります
        continue-on-error: true # エラーが出てもジョブが失敗にならないようにし、次に進む。

      - name: Terraform Init
        id: init
        working-directory: ./terraform
        run: terraform init # initを実行してTerraformを実行できるようにします。

      - name: Terraform Validate
        id: validate
        working-directory: ./terraform 
        run: terraform validate -no-color # 事前にvalidate確認を実施します。-no-colorを指定しないとログの出力がおかしくなるようです。

      - name: Terraform Plan
        id: plan
        working-directory: ./terraform
        run: terraform plan -no-color # 実行内容を確認します。
        continue-on-error: true

      - name: Terraform Apply
        id: apply
        working-directory: ./terraform
        run: terraform apply -auto-approve # applyします。-auto-approveで実行確認の入力が不要となる

GitHub ActionsでTerraformを使用する際は、hashicorpのsetup-terraformが参考になります。

流れとしては、Terraformをインストールして、リソース定義ファイルをチェックして、applyするだけです。

terraform fmt -checkコマンドは、フォーマット対象が存在すると終了ステータスが3(0が正常終了を表す)になってエラーとなります!

terraform validateコマンドも同様で、定義ファイルに問題があると終了ステータスが1となり、エラーになります!

  • 今回は簡単のためにapplyまで実行していますが、任意のタイミングで実行したい場合は実行しないということもできます
  • CI/CDをどのように構成するか次第です

実際に動かしてみよう

ワークフローを作成したので、実際に動かしてみましょう!

テスト用ファイル作成

ワークフローではterraformディレクトリでTerraformを動かす想定なので、ディレクトリの作成からリソース定義ファイルの作成まで実施していきます

  • terraformディレクトリを作成します
$ pwd
/home/ec2-user/environment/GitHubActionsTestByOIDC
$ mkdir terraform
$ cd terraform/ 
  • main.tfを作成します
    詳細はコメントの通りです
terraform {
  # 使用するAWSプロバイダーのバージョン指定(結構更新が速い)
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~>3.72"
    }
  }
}

# 明示的にAWSプロバイダを定義(暗黙的に理解してくれるけど)
provider "aws" {
  profile = "default"
  region  = "ap-northeast-1"

  # 作成する全リソースに自動的に付与するタグ設定
  default_tags {
    tags = {
      env = "GitHubActionsTestByOIDC"
    }
  }
}
  • テスト用にS3バケットを作成するリソース定義S3.tfを作成します
    テスト用に作成するだけなので、詳細は割愛します
# -----------------------------------
# S3の作成
# -----------------------------------
### バケット作成 ###
resource "aws_s3_bucket" "terraform_test" {
  # S3のバケット名
  bucket = "test-bucket-20220202-githubactionstestbyoidc" ###要変更###
  # アクセス管理
  acl = "private"
  # バージョニングの有効化
  versioning {
    enabled = true
  }

  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}

resource "aws_s3_bucket_public_access_block" "terraform_test" {
  # 対象のバケット
  bucket = aws_s3_bucket.terraform_test.id
  # パブリックのアクセスをブロック
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}
  • 作成した内容をGitHubにPushします
$ git add .
$ git commit -m "テスト用のTerraformのリソース定義ファイル作成"
$ git push 
  • ここまでの作業でディレクトリ構造は以下のようになっています

これで準備は整いました!

ワークフローの実行

今回の作成したワークフローはプルリクをマージした際に動作します。
なので、適当にブランチを作成してプルリクマージを行います。

  • ブランチを作成して、README.mdを編集してプッシュまでします
$ pwd
/home/ec2-user/environment/GitHubActionsTestByOIDC
$ git checkout -b github_test
Switched to a new branch 'github_test'
$ echo "GitHub workflow test." >> README.md 
$ git add .
$ git commit -m "GitHubワークフローのテスト用コミット"
$ git push -u origin github_test 
  • GitHubのリポジトリのWEBページにアクセスしてプルリクを作成画面に移動します
  • プルリクのコメントは適当でいいので、プルリクエストを作成
  • 自分管理のリポジトリなので、そのままマージ!!!
  • GitHub Actionsで実行されるので、Actionsタブからワークフローの実行状況が見れます
  • 正常に実行されたらになります!
  • AWSのS3でバケットが作成されていることが確認できました!!
  • 作成したバケットは、上記のやり方だとTerarformから削除することができないので手動で削除してくださいー
  • ワークフローから削除可能にするには、tfstateファイルをS3など外部に保存してやる必要があります

まとめ

この記事ではGitHub ActionsでOIDC認証をしてTerrafomを実行してみました。

OIDC認証はアクセスキーID・シークレットアクセスキーを使用するよりかは安全なので、今後の主流になりそうな気がします。
みなさんには是非使ってほしいですね。

もし「設定できない!」や「うまくいかない!」などがありましたら著者のTwitterまでご連絡ください。

参考サイト・書籍

ワークフローでのOIDC認証

Zenn GitHub Actions の OpenID Connect サポートについて
GitHub Actions + OIDC Token の情報をAWSのセッションタグに設定してみた
GitHub aws-actions/configure-aws-credentials

GitHub Docs Configuring OpenID Connect in Amazon Web Services ⇒GitHub ActionsでOIDCを利用するための公式ドキュメント。とりあえずこれに従っとけ内容

IAM ロールの PassRole と AssumeRole をもう二度と忘れないために絵を描いてみた ⇒IAMロールについて、とにかく勢いで分かりやすい!

AWSの認証情報なしでGitHub Actionsからアクセスする with Terraform ⇒細かなところまでわかる

GitHub Actions の OIDC トークンの sub にはなにが入るのか? ⇒GitHubのOIDCトークンのsubの設定内容が参考になる

DeveloperIO 【小ネタ】GitHub Actions用のIAMロールをAWSマネジメントコンソールから作成する際の注意点 & [要注意]GitHub Actions OIDC+AWS IAMロールで”token.actions.githubusercontent.com:sub”条件を書き忘れてはいけない ⇒Conditionを設定しない場合にどうなるかなどわかりやすい

GitHub Actionsのワークフロー

GitHub Docs GitHub Actionsのワークフロー構文 ⇒公式の解説なのでリファレンス

GitHub hashicorp/setup-terraform ⇒hashicorpのが提供しているGitHub Actionsのサンプル。Planの結果をプルリクのコメントに載せる方法もある!

Zenn GitHub ActionsでTerraformの実行を自動化する ⇒TerraformをGitHub Actionsで実行するわかりやすい例