LIFULL Creators Blog

LIFULL Creators Blogとは、株式会社LIFULLの社員が記事を共有するブログです。自分の役立つ経験や知識を広めることで世界をもっとFULLにしていきます。

GitHubトークン管理におけるセキュリティと利便性の向上

本記事は LIFULL Advent Calendar 2023 の17日目の記事です。 qiita.com

事業基盤のチームのマネジメントを担当している磯野です。
自他共に認めるGitHubおじさんとして社内では活動しています。

私たちのチームは開発生産性をより高めるため、開発エコシステムの改善に取り組んでおり、特にGitHubを中心とした生産性向上に注力しています。

今回はその取り組みの一環として、GitHub Actions においてマシンユーザーやAppsを減らしつつ、セキュリティと利便性を向上させるための施策について紹介します。

GitHub Actionsでの課題

GitHub Actionsを用いた処理の実行に際し、いくつかの課題に直面しました。

課題としては以下のような点があります:

  • 標準で使用可能なGITHUB_TOKENではworkflowのトリガーが不可能で、また.github/workflowsディレクトリの更新もできない
  • マシンユーザーやGitHub Appsの管理にコストがかかる
  • マシンユーザーやGitHub Appsを利用するには、リポジトリごとにシークレットを設定し、すべての利用箇所に対して必要な権限を付与する必要があり、セキュリティ上のリスクが存在する

これらの課題への解決策として、GitHub Actionsでの認証情報の設定を単純化する処理を開発しました。

これにより、ワークフローに必要な権限のみを付与することでセキュリティの向上が期待できます。

課題解決後の利用シナリオ

GitHub Actionsでの認証情報の設定を単純化することで、次のようなことが可能になりました:

  • GitHub ActionsからのpushまたはPR作成時において、Actionsをトリガーすることが可能
  • .github/workflows/ディレクトリ内のファイル更新が可能
  • 標準のGITHUB_TOKENではできない処理も簡易に実行可能

また、リポジトリ側でシークレット情報を管理する必要がない ため、シークレットを各リポジトリへ配布する必要がなく、運用上のメリットもあります。
弊社ではマイクロサービス化が進むにつれてRepositoryが増加し、それぞれに対しマシンユーザーを個別に追加することで運用コストが肥大化しており、大きな運用コスト削減につながっています。

課題を解決するための仕組み

GitHub Actionsでの認証情報を単純化するため、以下のような仕組みを開発しました。実線はActions内の処理の流れを、点線は各処理からのAPI呼び出しを表しています。

graph TB

subgraph GitHub Actions
  direction TB
  START((開始))
  ACTION1("aws-actions/configure-aws-credentials<br>次のLambda呼び出し用")
  ACTION2("configure-github-credentials<br>(今回作成したもの)")
  ACTION3("実処理")
  END((終了))
end
subgraph GitHub API
  direction TB
  API1("POST /app/installations/{installation_id}/access_tokens")
  API2("その他のAPI")
end

subgraph AWS
  direction TB
  STS["AWS STS<br>後続のLambdaを呼び出すトークンを取得"]
  LAMBDA["AWS Lambda<br>(IDトークンの検証と<br>GitHubトークンの取得)"]
end

START --> ACTION1
ACTION1 --> ACTION2
ACTION2 --> ACTION3
ACTION3 --> END
ACTION1 -."①後続のLambdaを呼び出す権限のある<br>AWSクレデンシャルの取得".-> STS
ACTION2 -."②後続のActionで利用するための<br>GitHubのTokenの取得".-> LAMBDA
LAMBDA -."③指定権限のトークン取得".-> API1
ACTION3 -..-> API2

Composite Actionとしての実装は以下のようになっています。

name: Create specified scoped token workflow
descroption: |
  Create specified scoped token workflow
inputs:
  role:
    required: true
    type: string
outputs:
  token:
    description: "GitHub App token"
    value: ${{ steps.create-app-token.outputs.token }}
runs:
  using: "composite"
  steps:
    - name: set env
      shell: bash
      run: |
        echo "LAMBDA_NAME="<<<lambda function name>>" >> $GITHUB_ENV
        echo "IAM_ROME_ARN="<<<iam role arn>>>" >> $GITHUB_ENV
    - uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: "${{ env.IAM_ROME_ARN }}"
        aws-region: "<<<region>>>"
    - id: create-app-token
      name: create app token
      shell: bash
      run: |
        export AWS_DEFAULT_REGION="<<<region>>>"
        ID_TOKEN="$(curl --silent -H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=configure-github-credentials" | jq -r '.value')"
        aws lambda invoke --cli-binary-format raw-in-base64-out --function-name "${{ env.LAMBDA_NAME }}" --payload "{
          \"id_token\": \"${ID_TOKEN}\",
          \"role\": \"${{ inputs.role }}\"
        }" outputfile.txt || :
        TOKEN=$(cat outputfile.txt | jq -re ".token // empty" || echo "")
        if [ -z "${TOKEN}" ]; then
          echo "Token is missing in the JSON file"
          cat outputfile.txt | jq -re ".errorMessage // empty"
          exit 1
        fi
        echo "::add-mask::$TOKEN"
        echo "GITHUB_TOKEN=$TOKEN" >> $GITHUB_ENV
        echo "token=$TOKEN" >> $GITHUB_OUTPUT

各処理の詳細

① 後続のLambdaを呼び出すためのAWSクレデンシャルの取得

aws-actions/configure-aws-credentials を用いて、後続のLambdaを呼び出すためのIAMロールのクレデンシャルを取得します。 事前にGitHubをIDプロバイダとして登録し、GitHub経由でAssumeRoleを実行できるよう設定しておく必要があります。

②GitHubのTokenの取得

IDトークンを用いた呼び出し元の検証を行い、Lambda上で適切な権限を付与します。

IDトークンの正式な検証を行うことで、トークン内に含まれるリポジトリ名やブランチ名、実行者などの情報が利用でき、指定の権限を付与すべき対象かどうかを判断します。

IDトークンから得られる情報についてはこちらを参照してください。

graph TB

subgraph configure-github-credentials
    direction LR
    CGC1("IDトークンの取得")
    CGC2("Lambdaの呼び出し")
    CGC3("トークンの環境変数への設定")
    CGC4("トークンをシークレットとして設定")
end

subgraph Lambda
    direction LR
    LAMBDA1("IDトークンの検証")
    LAMBDA2("指定の権限が利用可能か検証")
    LAMBDA3("GitHubトークンの取得")
    LAMBDA4("GitHubトークンの返却")
end

CGC1 --> CGC2
CGC2 --"IDトークン, 必要な権限"--> LAMBDA1
LAMBDA4 --"GitHubトークン、有効期限"--> CGC3
CGC3 --> CGC4

LAMBDA1 --> LAMBDA2
LAMBDA2 --> LAMBDA3
LAMBDA3 --> LAMBDA4

③ 指定権限のトークン取得

②で述べたLambdaから呼び出されるAPIです。 GitHub AppsのInstallation access tokenを生成します。このAPIを呼び出す際はApps側にその権限が必要です。

今後の展望と課題

この仕組みは現在一部のプライベートリポジトリで利用を開始していますが、将来的にはさらに拡張し、最終的にはオープンソースとして公開したいと考えています。

まとめ

GitHubのOIDCとAWS Lambdaを活用し、GitHub AppsのInstallation access tokenを安全に取得できるようになりました。これにより、GitHub Actionsでの認証情報のセットアップを単純化し、よりセキュアな実行が可能です。

LIFULLでは開発生産性向上のための開発エコシステムの改善に積極的に行っています。今回の仕組みはその一環であり、AIを活用して全社的な生産性向上を目指すkeelaiや、Kubernetesを用いたアプリケーション実行基盤であるKEELなど、多くの取り組みを進めています。

LIFULLでは一緒に働いてくれる仲間を募集しています。これらの取り組みに興味を持った方がいらっしゃいましたら以下からぜひお問い合わせください。

hrmos.co