CTO室SREの @sinsoku です。
ドキュメントをテックブログとして書いておくと一石二鳥なことに気づいたので、ここに書きます。
GitHub Appsが必要なユースケース
GitHub Actions で使用できる secrets.GITHUB_TOKEN
には以下の制限があります。
secrets.GITHUB_TOKEN
を使用した操作では新しいワークフローが実行されません- Actions で作成したプルリクで Actions のテストが動かない
- 他リポジトリのコードを参照できません
- プライベートリポジトリのgemやnpmの取得ができない
この問題を回避するため、GitHub Appsで一時的なアクセストークンを生成して使用します。
なぜ Personal Access Token(PAT)を使うべきではないか?
PATを使用することは以下の理由から推奨していません。
- PATを作成した人が退職した場合、引き継ぎを忘れると動かなくなる
- PATの有効期限の管理が煩雑になる
- 無期限にするのはセキュリティ上好ましくない
- マシンユーザーの人数分の費用が増加する
- アカウントを使い回すのはセキュリティ上好ましくない*1
GitHub Apps の作成手順
公式ドキュメント
GitHub Docs に手順が記載されているので、まだ読んだことない方は一度目を通しておいてください。
作成手順
- Register new GitHub App で各項目を埋めてください
項目 値 GitHub App name 適当な名前
(弊社だとmpg-xxx-bot
の命名規則)Homepage URL 適当なURL
(弊社だと https://github.com/medpeer-dev )Webhook 不要なので Active
のチェックを外すRepository permissions Permissions required for GitHub Apps を参照して必要な権限を選択してください。
例: コードを参照する場合はContents
、プルリクを作る場合はPull requests
など
- GitHub Apps 作成したら、秘密鍵を生成する
- 使用したいリポジトリの Secrets に以下を設定する
- GitHub AppsのID
- 生成した秘密鍵
Orgに所有権を委譲する
作成したGitHub Appsの所有権をOrgに委譲し、必要なリポジトリで使えるようにOrgにインストールする必要があります。
公式ドキュメント
GitHub Docs に所有権の委譲の手順があるため、参照してください。
Ownerへの依頼手順
弊社ではSREメンバーがOwner権限を持っているため、以下のような運用にしています。
- 作成したGitHub Appsの所有権をOrgに委譲する
- Backlogで以下を記載したチケットを作成する*2
- GitHub Appsの用途
- GitHub Appsをアクセスするリポジトリ一覧
- GitHub Appsの管理者アカウント
SREメンバーは 用途 と 権限 に問題がなければ、所有権の委譲リクエストを承認します。 承認した後、以下の作業をします。
- GitHub Apps に管理者を追加する
- GitHub Apps をOrgにインストールする
Only select repositories
で指定のリポジトリだけ許可する
アクセストークンの使い方
GitHub Actionsでの利用
tibdex/github-app-token を使用します。
README の引用ですが、以下のように簡単に使用できます。
- name: Generate token id: generate_token uses: tibdex/github-app-token@v1 with: app_id: ${{ secrets.APP_ID }} private_key: ${{ secrets.PRIVATE_KEY }} # Optional (defaults to ID of the repository's installation). # installation_id: 1337 # Optional (defaults to the current repository). # repository: "owner/repo" - name: Use token env: TOKEN: ${{ steps.generate_token.outputs.token }} run: | echo "The generated token is masked: ${TOKEN}"
CircleCIでの利用
CircleCI では簡単に扱う方法がないため、以下のスクリプトを使用します。
#!/usr/bin/env ruby # frozen_string_literal: true # GitHub Appsで使うアクセストークンを生成し、標準出力に表示するスクリプト。 # デフォルトgemではないjwtを入れる require 'bundler/inline' gemfile do source 'https://rubygems.org' gem 'jwt' end require 'openssl' require 'net/http' require 'json' require 'jwt' # 環境変数からAPP_ID, PRIVATE_KEYを読み込む。 gh_app_id = ENV['GITHUB_APPS_ID'] # Circleでは複数行の値を環境変数に使えないため、Base64でエンコードして設定 gh_private_pem_base64 = ENV['GITHUB_APPS_KEY_BASE64'] gh_private_pem = Base64.decode64(gh_private_pem_base64) payload = { iat: Time.now.to_i - 60, exp: Time.now.to_i + (10 * 60), iss: gh_app_id } private_key = OpenSSL::PKey::RSA.new(gh_private_pem) jwt = JWT.encode(payload, private_key, "RS256") # httpリクエストを投げるのに必要な変数を用意 headers = { Authorization: "Bearer #{jwt}", Accept: "application/vnd.github.v3+json" } http = Net::HTTP.new('api.github.com', 443).tap { |h| h.use_ssl = true } # GitHubのAPIでアクセストークンを生成する # # - https://docs.github.com/en/rest/apps/apps#get-a-repository-installation-for-the-authenticated-app # - https://docs.github.com/en/rest/apps/apps#create-an-installation-access-token-for-an-app installation = http.get("/repos/medpeer-dev/medpeer/installation", headers).then { |r| JSON.parse(r.body) } access_token = http.post("/app/installations/#{installation["id"]}/access_tokens", {}.to_json, headers).then { |r| JSON.parse(r.body) } # アクセストークンを出力 puts access_token["token"]
スクリプトの出力を環境変数に設定することで、プライベートリポジトリにアクセスできます。
- run: name: Set GitHub access token command: | export GITHUB_ACCESS_TOKEN="`./bin/gh_apps_token`" export BUNDLE_GITHUB__COM="x-access-token:${GITHUB_ACCESS_TOKEN}"
参考ページ
メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!
■募集ポジションはこちら
https://medpeer.co.jp/recruit/entry/
■開発環境はこちら