【CI/CDシリーズ➂】OpenID Connectを利用したAWSとGithub Actionsのセキュアな接続(CloudFormationの自動デプロイ)

CI/CDシリーズとして、GitHub Actionsを用いたAWSインフラおよびコンテナアプリケーションの構築やテストの自動化について解説します。

第三回では、IAMにOIDCを利用したセキュアなGitHubとの接続と、CloudFormationの自動デプロイを行っていきます。

  • プルリクエストされたCloudFormationテンプレートの自動デリバリー・デプロイ

dashboard

OpenID Connectとは

OpenID Connect(OIDC)は、OAuth 2.0の上に構築された認証プロトコルで、ユーザーのログインとID情報の取得を簡単かつ安全に行います。ユーザーは認証プロバイダーを通じてログインし、アプリケーションは認証コードを用いてアクセストークンとIDトークンを取得します。これにより、アプリケーションはユーザー情報を確認し、適切なサービスを提供できます。

GitHubとAWSの場合、具体的には、以下のような仕組みで、認証が実現されています。

詳細はAbout security hardening with OpenID Connectを参照ください。

dashboard

この図は、GitHub ActionsがOpenID Connect (OIDC)を使用してAWSと連携する仕組みを示しています。各ステップの詳細を以下に説明します。

1. OIDC Trust

  • 説明:クラウドプロバイダー(AWS)は、GitHub OIDCプロバイダーを信頼する設定を行います。これにより、GitHub Actionsが発行するIDトークンを受け入れることができます。
  • 具体例:AWS IAM(Identity and Access Management)で信頼ポリシーを設定し、GitHub Actionsから発行されたOIDCトークンを受け入れるための信頼関係を構築します。

2. GitHub Actions Workflow と GitHub OIDC Provider

  • 説明:GitHub Actionsワークフローが実行される際、GitHub OIDCプロバイダーを介してOIDCトークンを生成します。
  • 具体例:ワークフロー内でpermissionsセクションにid-token: writeを設定し、必要な場合にOIDCトークンを取得します。

3. JSON Web Token (JWT) Cloud Role ID

  • 説明:GitHub Actionsワークフローは、GitHub OIDCプロバイダーから取得したJSON Web Token (JWT)を使用して、クラウドプロバイダー(AWS)に対して認証を行います。このJWTには、クラウドロールIDやその他のクレームが含まれています。
  • 具体例:aws-actions/configure-aws-credentials@v4アクションを使用して、GitHubから取得したJWTをAWSに渡し、指定されたロールを引き受けるための一時的な認証情報を取得します。

4. Access Token

  • 説明:クラウドプロバイダー(AWS)は、受け取ったJWTを検証し、アクセスを許可する場合はアクセストークンを発行します。このアクセストークンを使用してクラウドリソースにアクセスします。
  • 具体例:GitHub Actionsワークフロー内でAWS CLIコマンドやSDKを使用してクラウドリソースを操作します。アクセストークンは一時的なもので、特定の操作に対してのみ有効です。

今回の構成

今回は以下画像の様な構成で、ローカルからリポジトリにテンプレートをプッシュすることで、AWS上のCloudFormationにテンプレートを渡して、スタックを作成します。

dashboard

AWS IAMで OIDCを設定する

早速AWS AIMからOIDCの設定をしていきます。

IAM → IDプロバイダ と遷移します。 『IDプロバイダの作成』をクリックします。

  • プロバイダURLには、「https://token.actions.githubusercontent.com」と入力
  • 対象者には、「sts.amazonaws.com」と入力

dashboard

IAM → ロール と遷移します。 『ロールを作成』 → 『カスタム信頼ポリシー』をクリックします。

以下jsonの内、を自分のものに合わせて修正し、入力します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::<account-id>:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                },
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": "repo:<github-name>/<repogitory-name>:*"
                }
            }
        }
    ]
}

『許可を追加』をクリックします。

AdministratorAccessを選択し、『ロールを作成』をクリックします。 (最小権限の原則としては、ECR FullAccessとCloudFormation FullAccessで十分です)

作成したロールのARNをコピーします。

続いて、GitHubを開き、Settings → Secret and variables → Actions と遷移します。『New repository secret』をクリックします。

  • Nameには、「AWS_ROLE_ARN」と入力
  • Secretには、IAMロールのARNを入力

dashboard

これで準備は完了です。

詳細はConfiguring OpenID Connect in Amazon Web Servicesを参照ください。

利用するコード・フォルダ構成

今回は、こちらのコード・GitHubを利用します。

※関係ないコードが入っている場合があります。その場合はクローンした際に削除してかまいません。

dashboard

フォルダ構成は以下の通りです。

※ワークフローフォルダはGitHub Actionsを利用するのに決まったフォルダ構成のため変更不可です。

Repogitory/
├── package.json
├── .github/                           # Github Actionsワークフローフォルダ
│   └── workflows/                     # Github Actionsワークフローフォルダ
│       └── deploy-cfn-ecr-stack.yml    
└── aws/             
    └── ecr.yml                        # CloudpFormationテンプレート

コードの説明(CI/CD 実装のための workflow のみ説明)

deploy-cfn-ecr-stack.yml

deploy-cfn-ecr-stack.ymlでは、aws/cloudformation/配下のecr.ymlに対して、プッシュか・プルリクエストが行われた場合に、aws cloudformation deploy APIを実行し、CloudFormationスタックをデプロイします。

なお、aws cloudformation deploy APIは、既にスタックが存在する場合はそのスタックをアップデートします。

name: Deploy CloudFormation ecr Stack

on:
  push:
    branches:
      - main
    paths:
      - 'aws/cloudformation/ecr.yml' 
  pull_request:
    branches:
      - main
    paths:
      - 'aws/cloudformation/ecr.yml' 
  • name:はワークフローの名前
  • on.pullrequest/push:はプルリクエストをトリガーに、ワークフローを起動すること
  • branch:は対象ブランチの指定
  • paths:はトリガーであるプルリクエストの対象のパス
  • つまり、aws/cloudformation/配下のecr.ymlがmainブランチにプッシュ・プルリクエストされた場合に、このワークフローが実行される
env:
  CFN_TEMPLATE: ecr.yml
  CFN_STACK_NAME: ecr-stack
  CFN_SYSTEM: cicd-handson
  CFN_ENV: test
  AWS_REGION: ap-northeast-1

permissions:
  id-token: write
  contents: read
  • env.xxx:この後利用する環境変数の定義
  • permissions:GitHub Actionsに必要な権限の定義
  • permisions.id-token.write: IDトークンの書き込み権限
    • GitHubのIDトークンを使ってAWSのロールを引き受けるために必要
  • permissions.contents.read: コンテンツの読み取り権限
    • actions/checkoutアクションを使用してリポジトリをクローンし、ファイルを取得するために必要
jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
  • jobs:は複数のジョブがあること
  • s3_sync:はジョブの名前
  • runs-on:はジョブの実行マシンを指定
  • steps:は各ジョブを実行していくこと
  • uses: actions/checkout@v4 は、GitHub Actionsがリポジトリのコードをクローンするためのアクションを使用すること
  • つまり、Ubuntsuマシン上で、レポジトリのコードをクローンしている
    - name: Configure AWS Credential
      uses: aws-actions/configure-aws-credentials@v4
      with: 
        role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
        aws-region: ${{ env.AWS_REGION }}

    - name: Deploy CloudFormation stack
      run: |
        aws cloudformation deploy \
          --template-file aws/cloudformation/${{ env.CFN_TEMPLATE }} \
          --stack-name ${{ env.CFN_STACK_NAME }} \
          --capabilities CAPABILITY_NAMED_IAM \
          --parameter-overrides \
            System=${{ env.CFN_SYSTEM }} \
            Env=${{ env.CFN_ENV }}        
  • uses: aws-actions/configure-aws-credentials@v4 はGitHub ActionsがAWSの認証情報を設定して、AWSのサービスにアクセスできるようにするためのアクションを使用すること(with以下で、各クレデンシャルやパラメーターを渡す)
  • role-to-assume:はGitHubシークレットに保存されているAWSロールARNを認証情報として使用すること
  • env:は環境変数として、secrets.S3_SYNC_BUCKETの値を受け取ること
  • runs:はコマンドを実行すること(echoやaws cliを実行する)
  • つまり、GitHubシークレットに格納したロールを引き受け、AWSへ認証を行い、aws cloudformation deploy APIを実行する

プッシュによるワークフローの起動

それでは、コードをレポジトリにプッシュして、CloudFormationスタックが作成されることを確認します。

下記コマンドを実行し、変更したコードをプッシュします。

git add .
git commit -m "add ecr stack template"
git push origin main

そうすると、下記画面の様に、ci.ymlとcd.ymlで定義したジョブが実行され、マージ可能になっています。

各ジョブの詳細は、ジョブの横のDetailをクリックするか、Actionsから確認することが出来ます。

dashboard

では、AWSにログインし、CloudFormationから、スタックが作成されていることを確認します。

dashboard

以上でハンズオン➂は終了です。今回は、OICDを利用したAWSとGitHubの連携・CloudFormationスタックの作成の自動化について、ハンズオンをしました。

ブログにしたいことがあるので、シリーズは一旦飛ぶかもしれません。