GitHub Apps のトークンを使ってプライベートリポジトリにアクセスする
はじめに
こんにちは。
DeNA 23新卒内定者 Advent Calendar 2022 20日目 🎄 を担当させていただきます FarStep です。
DeNA 23新卒内定者 Advent Calendar 2022 では、本記事の公開日(2022/12/20)以降もさまざまなジャンルの技術に関する記事が更新されるのでぜひご覧ください!
本記事の目標
本記事の目標は、GitHub Apps で生成したトークンを使って、GitHub 上のプライベートリポジトリへの認証ができる です。
「プライベートリポジトリへの認証ができるようになる」ということは、例えば GitHub Actions を使って CI を構築する際に、「プライベートリポジトリに対して自由に操作ができるようになる」ということです。
想定する状況
あるレポジトリで Go 言語 を使った チーム開発 を行なっているとします。
そのレポジトリで、「最新イメージを ECR に push する」という CI を GitHub Actions を用いて構築する場合を想定します。
CI の流れは下記の通りです(一部省略しています)。
- OIDC を用いて AWS 認証を行う
- ECR にログインする
-
Dockerfile 内の
go mod download
を実行する ← 本記事のメインテーマ - Dockerfile 内の
go build
を実行する - ECR へ最新イメージを push する
「3. Dockerfile 内の go mod download
を実行する」というステップで、別のプライベートリポジトリを参照しています。チーム開発をする場合、全てのリポジトリは Organization 内にあるかと思いますので、図解すると下記のような感じですね。
つまり、
go.mod
Dockerfile
.github/workflows/build.yml
に下記のようなコードが記述されているということです。
module github.com/organization/my-repository
go 1.18
require (
// プライベートリポジトリを参照している
github.com/organization/another-repository v0.0.0-xxxxx
)
require (
github.com/xxxxx/xxxxx v0.0.0 // indirect
github.com/xxxxx/xxxxx v0.0.0 // indirect
github.com/xxxxx/xxxxx v0.0.0 // indirect
)
最初の require
で、プライベートリポジトリを参照していますね。
github.com/organization/another-repository
の organization
には Organization の名前、another-repository
には Organization 内の参照したいプライベートリポジトリの名前が入ります。
FROM golang:1.18
WORKDIR /go/src/app
COPY . .
# ダウンロードするモジュールの中には、プライベートリポジトリのモジュールも含まれる
RUN go mod download
RUN go build ./cmd/main.go
EXPOSE 8080
CMD ["/go/src/app/main"]
go mod download
を実行してダウンロードするモジュールには、github.com/organization/another-repository
というプライベートなモジュールも含まれます。
name: "CI"
on:
push:
branches:
- develop
permissions:
id-token: write
contents: read
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Assume Role
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.DEV_GITHUB_ACTIONS_OIDC_ARN }}
aws-region: ap-northeast-1
- name: Login to ECR
uses: docker/login-action@v1
with:
registry: ${{ secrets.ECR_REGISTRY }}
- name: Build and push
uses: docker/build-push-action@v3
with:
push: true
tags: ${{ secrets.ECR_REGISTRY }}/${{ secrets.ECR_REPOSITORY }}:${{ github.sha }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
name: Build and push
のステップで、Dockerfile
に記述された手順通りにビルドを行っています。
解決したい問題
さて、先ほど示した CI には一つ問題があります。
それは、プライベートリポジトリへの認証がされていない という点です。
CI の中でビルドを実行する際に、プライベートリポジトリのモジュールをダウンロードしているため、プライベートリポジトリへのアクセスが許可されていないと CI が失敗します。
具体的には GitHub Actions のログに、下記のようなエラーが吐かれます。
remote: Repository not found.
fatal: Authentication failed for 'https://github.com/organization/private-repository/'
fatal: could not read Username for 'https://github.com': terminal prompts disabled
問題の解決方法
前節での問題を解決する方法として、GitHub Apps で生成したトークンを使ったプライベートリポジトリへの認証 をご紹介します。
GitHub Apps とは
GitHub Apps は、Organization や個人アカウントに直接インストールでき、特定のリポジトリへのアクセス権を付与することが可能です。GitHub Apps の主な特長は次の三つです。
- アクセス権限を細かく設定することができる。
- インストール単位が User/Organization の保持するリポジトリ単位になる。
- リポジトリにおけるイベントの発生も受け取ることができる(Webhook を備える)。
また GitHub Apps は、パーソナルアクセストークン(personal access token)と異なり、個人アカウントに紐づかないので会社組織等で使う際の管理に向いています。
パーソナルアクセストークンを採用してしまうと、ユーザーに紐付いたアクセスキーが発行されてしまうため、設定したユーザーが退職したり、異動したりしてアカウントが停止されたりするとアクセスキーも無効になり、認証エラーになってしまいます。
GitHub Apps の作成
それではトークンを発行する GitHub Apps を作成しましょう。
https://github.com/{organization} にアクセスしてください。
{organization} には、Organization の名前が入ります。
「Settings」タブをクリックしたあと、下までスクロールしてください。
サイドバーに「GitHub Apps」というメニューがありますので、クリックしてください。
すると、https://github.com/organizations/{organization}/settings/apps に遷移します。
上記のページで「New GitHub App」をクリックしてください。
すると、https://github.com/organizations/{organization}/settings/apps/new に遷移します。
上記フォームに必須項目を入力してください。
今回、Webhook の設定は行いませんので、Webhook の Active のチェックは外してください。
項目 | 値 |
---|---|
GitHub App name | 任意の名前(例. my-github-app) |
Homepage URL | 任意のURL(例. https://example.com) |
Webhook の Active | チェックを外す |
フォームへの入力が完了しましたら、いよいよ権限の設定です。
「Repository permissions」をクリックすると、アコーディオンメニューが開きます。
今回構築する CI では、go mod download
実行時にリポジトリの中身を参照するだけですので、「Repository permissions」の「Contents」を「Access: Read-only」に変更してください。「Contents」を「Access: Read-only」に変更すると、自動的に「Metadata」も「Access: Read-only」に変更されます。
権限の設定が完了したら、「Create GitHub App」をクリックしてください。
作成が完了すると、下記のような画面に遷移します。
現在表示されている「App ID」の値を後ほど使います。
「App ID」に加えて「private key」を取得する必要があります。
下にスクロールして「Generate a private key」をクリックしてください。
すると、my-github-app.2022-12-15.private-key.pem
というファイルがダウンロードされるはずです(ファイル名は GitHub App の名前、作成日時によって異なります)。
このファイルに記述されている秘密鍵を後ほど使います。
これで、GitHub Apps の作成が完了です 🎉
GitHub Apps のインストール
次に、作成した GitHub Apps を Organization 内のリポジトリにインストールしましょう。
下記画面のサイドバーの「Install App」メニューをクリックしてください。
インストール先の Organization を選択して「Install」をクリックしてください。
Organization 内には、複数のレポジトリが存在するかと思います。
今回は、CI を構築しているリポジトリ・CI で参照するもう一つのレポジトリのみに GitHub Apps のインストールします。
GitHub Apps をインストールするリポジトリは最小限にとどめておきましょう。
これで、GitHub Apps のインストールが完了です 🎉
GitHub Apps によるトークンの生成
続いて、GitHub Actions のワークフロー内で GitHub Apps によるトークンの生成を行いましょう。
最初に、GitHub Apps 作成時に取得した「App ID」と「private key」を CI を構築するレポジトリの Secrets に登録します。
CI を構築するレポジトリの「Settings」に移動した後、サイドバーの「Secrets」の「Actions」をクリックしてください。
「New repository secret」をクリックして、「App ID」と「private key」を登録してください。
キーの名前は、「APP_ID」・「PRIVATE_KEY」としましょう。
下記のように、「APP_ID」・「PRIVATE_KEY」が追加されていれば OK です。
これで、GitHub Apps によるトークンの生成の準備が整いました。
次に、.github/workflows/build.yml
を編集します。
name: "CI"
on:
push:
branches:
- develop
permissions:
id-token: write
contents: read
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
+ - name: Generate token
+ id: generate_token
+ uses: tibdex/github-app-token@v1
+ with:
+ app_id: ${{ secrets.APP_ID }}
+ private_key: ${{ secrets.PRIVATE_KEY }}
- name: Assume Role
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.DEV_GITHUB_ACTIONS_OIDC_ARN }}
aws-region: ap-northeast-1
- name: Login to ECR
uses: docker/login-action@v1
with:
registry: ${{ secrets.ECR_REGISTRY }}
- name: Build and push
uses: docker/build-push-action@v3
with:
+ build-args: |
+ "TOKEN=${{ steps.generate_token.outputs.token }}"
push: true
tags: ${{ secrets.ECR_REGISTRY }}/${{ secrets.ECR_REPOSITORY }}:${{ github.sha }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
name: Generate token
というステップを追加しました。
GitHub Apps によるトークンの生成には、tibdex/github-app-token を使用しています。
app_id
と private_key
に、先ほど Secrets に登録した値を渡しています。
また name: Build and push
のステップでは、生成したトークンをビルド時に使用可能な ARG
に渡しています。
これで、Dockerfile 内で GitHub Apps が生成したトークンを使えるようになりました 🎉
トークンによる認証
それでは最後に、生成したトークンを使ってプライベートリポジトリへの認証を行いましょう。
Dockerfile
を編集します。
FROM golang:1.18
+ ARG TOKEN
WORKDIR /go/src/app
COPY . .
+ RUN export GOPRIVATE=github.com/organization
+ RUN git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "https://github.com/"
RUN go mod download
RUN go build ./cmd/main.go
EXPOSE 8080
CMD ["/go/src/app/main"]
追加したコードについて説明します。
プライベートなモジュールを取得するときに checksum の検証ができないため、下記コードで GOPRIVATE
という環境変数を設定しています。organization
は、Organization の名前にしてください。
RUN export GOPRIVATE=github.com/organization
そしてトークンを利用するように、下記コードで git config
の設定を行なっています。
https://github.com/
という URL を使用する際に別 URL に置き換えるための設定です。これで、常にトークンを使用して GitHub にアクセスするようになりました。
ARG TOKEN
RUN git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "https://github.com/"
以上で、トークンによるプライベートリポジトリへの認証が完了です 🎉
go mod download
が成功するため、CI は正常に動くようになります。
おわりに
いかがだったでしょうか。
GitHub Apps で生成したトークンを使った認証について理解できたでしょうか。
本記事で紹介した状況は一例です。GitHub Apps の権限を変更することで、様々な GitHub のサービスに対して認証を行うことが可能です。
是非、GitHub Apps を活用してみてください。
参考文献
Discussion