[GitHub Actions] 三項演算子ライクな記述でブランチ名に応じて必要なシークレットのみ取得する

2022.04.22

こんにちは、CX事業本部 IoT事業部の若槻です。

GitHub Actionsで、不必要なシークレットまで取得するWorkflowを組んでしまっていませんか?私は今まで組んでしまっていました。

そこで今回は、GitHub Actionsで三項演算子(condition ? exprIfTrue : exprIfFalse)ライクな記述を使用して、ブランチ名に応じて必要なシークレットのみ取得するWorkflowを作ってみました。

不必要なシークレットまで取得してしまうパターン

AWSの開発/ステージング/本番環境の認証情報を使用してシステムのデプロイを行うWorkflowを想定します。開発はdevelop、ステージングはstaging、本番はmainのブランチにマージが行われた時にそれぞれの環境にデプロイが行われる想定です。

.github/workflows/cicd.yaml(不必要なシークレットを取得してしまうパターン)

on:
  push:
    paths-ignore:
      - '**/*.md'

jobs:
  integration:
    runs-on: ubuntu-latest
    steps: 
      - run: echo "中略"

  deploy:
    runs-on: ubuntu-latest
    needs: integration
    if: |
      github.ref_name == 'develop' ||
      github.ref_name == 'staging' ||
      github.ref_name == 'main'
    env:
      DEV_AWS_OIDC_ROLE_ARN: ${{ secrets.DEV_AWS_OIDC_ROLE_ARN }}
      STG_AWS_OIDC_ROLE_ARN: ${{ secrets.STG_AWS_OIDC_ROLE_ARN }}
      PRD_AWS_OIDC_ROLE_ARN: ${{ secrets.PRD_AWS_OIDC_ROLE_ARN }}
      AWS_REGION: us-east-1
    permissions:
      id-token: write
      contents: read
    steps:
      - name: debug
        run: |
          echo Branch: ${{ github.ref_name }}
          echo DEV_AWS_OIDC_ROLE_ARN: ${DEV_AWS_OIDC_ROLE_ARN/::*:/::XXXXXXXXXXXX:}
          echo STG_AWS_OIDC_ROLE_ARN: ${STG_AWS_OIDC_ROLE_ARN/::*:/::XXXXXXXXXXXX:}
          echo PRD_AWS_OIDC_ROLE_ARN: ${PRD_AWS_OIDC_ROLE_ARN/::*:/::XXXXXXXXXXXX:}

      - name: Assume Role DEV
        if: ${{ github.ref_name == 'develop' }}
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ env.DEV_AWS_OIDC_ROLE_ARN }}
          aws-region: ${{env.AWS_REGION}}

      - name: Assume Role STG
        if: ${{ github.ref_name == 'staging' }}
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ env.STG_AWS_OIDC_ROLE_ARN }}
          aws-region: ${{env.AWS_REGION}}

      - name: Assume Role PRD
        if: ${{ github.ref_name == 'main' }}
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ env.PRD_AWS_OIDC_ROLE_ARN }}
          aws-region: ${{env.AWS_REGION}}

      # checkout & some build & deploy

envでは開発/ステージング/本番環境それぞれの認証情報をシークレットからすべて取得して環境変数に格納するようになっています。

.github/workflows/cicd.yaml(不必要なシークレットを取得してしまうパターン)

    env:
      DEV_AWS_OIDC_ROLE_ARN: ${{ secrets.DEV_AWS_OIDC_ROLE_ARN }}
      STG_AWS_OIDC_ROLE_ARN: ${{ secrets.STG_AWS_OIDC_ROLE_ARN }}
      PRD_AWS_OIDC_ROLE_ARN: ${{ secrets.PRD_AWS_OIDC_ROLE_ARN }}

認証情報を使用した認証(AssumeRole)処理では、ブランチに応じてどの環境変数を使用して認証を行うかの条件分岐を書いています。

.github/workflows/cicd.yaml(不必要なシークレットを取得してしまうパターン)

      - name: Assume Role DEV
        if: ${{ github.ref_name == 'develop' }}
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ env.DEV_AWS_OIDC_ROLE_ARN }}
          aws-region: ${{env.AWS_REGION}}

      - name: Assume Role STG
        if: ${{ github.ref_name == 'staging' }}
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ env.STG_AWS_OIDC_ROLE_ARN }}
          aws-region: ${{env.AWS_REGION}}

      - name: Assume Role PRD
        if: ${{ github.ref_name == 'main' }}
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ env.PRD_AWS_OIDC_ROLE_ARN }}
          aws-region: ${{env.AWS_REGION}}

そしてこのWorkflowを使用して、例えばfeature/xxxxxxxブランチからdevelopブランチへマージすると、開発環境に対するAssumeRoleが行われますが、シークレットの取得は全環境に対して行われてしまいます。.../dev-github-oidc-roleが開発、.../stg-github-oidc-roleがステージング、.../prd-github-oidc-roleが本番環境用の認証情報です。

このシークレットをすべて取得する処理は認証情報を不必要に暴露させるため、セキュリティリスクに繋がります。開発環境へのデプロイを行うのに、ステージングおよび本番環境の認証情報をシークレットから取得する必要はないはずです。

必要なシークレットのみ取得してみる

そこで、Workflowを次のように修正します。

.github/workflows/cicd.yaml

on:
  push:
    paths-ignore:
      - '**/*.md'

jobs:
  integration:
    runs-on: ubuntu-latest
    steps: 
      - run: echo "中略"

  deploy:
    runs-on: ubuntu-latest
    needs: integration
    if: |
      github.ref_name == 'develop' ||
      github.ref_name == 'staging' ||
      github.ref_name == 'main'
    env:
      AWS_OIDC_ROLE_ARN: |
        ${{
          github.ref_name == 'main' &&
            secrets.PRD_AWS_OIDC_ROLE_ARN ||
          github.ref_name == 'staging' &&
            secrets.STG_AWS_OIDC_ROLE_ARN ||
          secrets.DEV_AWS_OIDC_ROLE_ARN
        }}
      AWS_REGION: us-east-1
    permissions:
      id-token: write
      contents: read
    steps:
      - name: debug
        run: |
          echo Branch: ${{ github.ref_name }}
          echo AWS_OIDC_ROLE_ARN: ${AWS_OIDC_ROLE_ARN/::*:/::XXXXXXXXXXXX:}

      - name: Assume Role
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ env.AWS_OIDC_ROLE_ARN }}
          aws-region: ${{env.AWS_REGION}}

      # checkout & some build & deploy

GitHub ActionsのExpressionsでは、次のような三項演算子ライクな記述が可能です。それを使用して単一の環境変数に対して現在のブランチ名に応じたシークレットの指定が可能です。

.github/workflows/cicd.yaml

    env:
      AWS_OIDC_ROLE_ARN: |
        ${{
          github.ref_name == 'main' &&
            secrets.PRD_AWS_OIDC_ROLE_ARN ||
          github.ref_name == 'staging' &&
            secrets.STG_AWS_OIDC_ROLE_ARN ||
          secrets.DEV_AWS_OIDC_ROLE_ARN
        }}

認証情報を使用した認証(AssumeRole)処理で、指定する環境変数はブランチによって変わらないため、一つの処理を済めば事足ります。

.github/workflows/cicd.yaml

      - name: Assume Role
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ env.AWS_OIDC_ROLE_ARN }}
          aws-region: ${{env.AWS_REGION}}

feature/xxxxxxxからdevelopへマージすると、開発環境用の認証情報がAWS_OIDC_ROLE_ARNに格納され、それを使用してAssumeRoleが行われました!

developからstagingへマージすると、ステージング環境用の認証情報がAWS_OIDC_ROLE_ARNに格納され、それを使用してAssumeRoleが行われました!

stagingからmainへマージすると、本番環境用の認証情報がAWS_OIDC_ROLE_ARNに格納され、それを使用してAssumeRoleが行われました!

おわりに

GitHub Actionsで三項演算子ライクな記述を使用して、ブランチ名に応じて必要なシークレットのみ取得するWorkflowを作ってみました。

GitHub ActionsのExpressionsは使いこなすと様々な表現が出来ることが分かり、いじっていて楽しかったです。こういう小さな積み重ねを大事にしたいですね。

参考

以上