1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHubプライベートリポジトリ内のGoモジュールを利用する方法

Posted at

この投稿では、プライベートリポジトリのGoモジュールを別のリポジトリでgo getしたりgo mod downloadする方法を解説する。

基礎

プライベートリポジトリのGoモジュールを使うには次の仕組みを理解する必要がある:

  • GOPRIVATE
  • GitHubの認証方式

ローカルで開発するにも、Dockerfileを書くにも、GitHub Actionsのワークフローを構築するにも、これらの知識が大前提となる。

GOPRIVATE

GOPRIVATEはGoに「このモジュールは非公開」と伝える仕組み。

GOPRIVATEをセットするにはgo envコマンドを実行する:

# 特定組織配下をすべて指定する場合:
go env -w GOPRIVATE="github.com/your-org/*"
# 個別に細かく指定する場合:
go env -w GOPRIVATE="github.com/your-org/*",github.com/your-user/your-repo

GOPRIVATEがどうなっているかもgo envで確認できる:

go env GOPRIVATE

GORPIVATEをセットすると何が起きる?

  • proxy.golang.orgなどを経由せず、GitHubから直接取ってくる
  • sum.golang.orgなどの公開チェックサムDBを使わなくなる

GOPRIVATEをセットしただけでは、プライベートリポジトリの中身は取れない。

GitHubの認証方式

GitHubの認証方式は、大きく分けて2つある。

  • トークン認証: PAT, OAuth, GitHub Apps
  • SSH秘密鍵認証: ~/.ssh, デプロイメントキー

Goがプライベートモジュールをダウンロードするには、どちらかの認証方式を設定する必要がある。

ローカル

macOSなどのローカル開発環境では、SSH秘密鍵認証を用いてプライベートリポジトリにアクセスするのがおすすめ。既にgit pushするためにSSHの設定がなされている可能性が高いため。

SSHでGitHubの認証ができるかは次のコマンドで確認できる:

ssh -T git@github.com

認証が通っているなら、go envGOPRIVATEを設定するだけ。go getでモジュールが取得できれば設定成功。

go get github.com/your-org/your-repo

Dockerfile

Dockerfileにおける認証方法は、トークン認証がオススメ。理由は、GitHub Actionsなどのビルド環境でもDockerfileが使われることがあるため。トークン認証にそろえておけば、ローカルでdocker buildするときも、CIでするときも同じDockerfileが使える。

# syntax=docker/dockerfile:1
# [1]

FROM docker.io/golang:1.24 AS builder

# [2]
ENV GOPRIVATE=github.com/your-org/*

COPY go.mod go.mod
COPY go.sum go.sum

# [3]
RUN --mount=type=secret,id=GH_TOKEN,env=GH_TOKEN \
    GIT_CONFIG_COUNT=1 \
    GIT_CONFIG_KEY_0=url."https://x-access-token:${GH_TOKEN}@github.com/".insteadOf \
    GIT_CONFIG_VALUE_0=https://github.com/ \
    go mod download

[1]: --mount=type=secretを有効にするために必要
[2]: GOPRIVATEにプライベートモジュールを指定する
[3]: --mount=type=secretでGitHubトークンを安全に受け取れるようにする。また、GIT_CONFIG_*環境変数でinsteadOfをセットすることで、go modGH_TOKENを使わせる。

ARGに機密情報はアブナイ

docker build --build-arg GH_TOKEN=... のように ARG/ENVでトークンを渡すのは非推奨。ARG/ENVはビルド履歴・レイヤ・キャッシュや最終イメージ/メタデータ経由で漏れうるため。

BuildKitの「シークレット/SSHマウント」を使うのが正解。Docker公式も「ARG/ENVでシークレットを渡すのは不適切。秘密はシークレット or SSHマウントを使え」と明記している。

https://docs.docker.com/build/building/secrets

なぜgit configではなくGIT_CONFIG_*なのか?

次のようなgit configコマンドでinsteadOfを設定する方法がなぜダメなのか説明する。

RUN --mount=type=secret,id=GH_TOKEN,env=GH_TOKEN \
    git config --global url."https://x-access-token:$GH_TOKEN@github.com/".insteadOf "https://github.com/" && \
    go mod download

git config --global ... はファイル書き込みが発生するのでレイヤにGH_TOKENが記録される可能性がある。もちろん、同一RUN内で削除すれば最終レイヤには残らないが、そもそもディスクに書かないほうが安全。

ローカルでdocker buildするには?

このDockerfileをローカルでビルドしてみるには、次のコマンドを使う。

GH_TOKEN=$(gh auth token) \
  docker buildx build --secret id=GH_TOKEN,env=GH_TOKEN .

大事なのは、--secretでGitHubの認証トークンの渡し方を指定すること。また、GH_TOKENには、ghコマンドの認証トークンを代入する方法が安全かつ手軽だ。

idの値GH_TOKENはDockerfileのidの値GH_TOKENと同じにする。envの値GH_TOKENdockerコマンドを実行するシェルの環境変数名と同じにする。この例では、全部GH_TOKENになっているが、次の例のように全部ばらばらでもいい:

# Shell
export A="hello world"                  # 環境変数Aに、値をセットする
docker buildx build --secret id=B,env=A # Secret Bに、環境変数Aの値をセットする

# Dockerfile
RUN --mount=type=secret,id=B,env=C \ # 環境変数Cに、Secret Bの値をセットする
    echo $C                          # 環境変数Cを表示する
#=> "hello world"

GitHub Actions

GitHub Actionsでの認証方式はいくつかある。

  • GitHub App Installation Access Token (オススメ)
  • Personal access token (PAT)
  • デプロイメントキー

この中でおすすめなのはGitHub App Installation Access Tokenだ。Personal access tokenは属人的なのでチーム開発には不向き。デプロイメントキーはリポジトリごとに設定しないといけず面倒、かつ、有効期限がないためリスクが高い。GitHub App Installation Access Tokenは、発行時にscopeやrepositoriesを指定して利用可能リソース範囲を絞れたり、有効期限が1時間と短くセキュア。

GitHub Appsの作成とインストール

GitHub App Installation Access Tokenを使うには、GitHub Appsを作成し、それを対象のorganizationにインストールする必要がある。go getするには、少くともContents:Readの権限をGitHub Appに与える。

また、GitHub Actionsで、GitHub Appの秘密鍵を使えるように、ActionsのSecretに秘密鍵を登録しておく必要もある。

ワークフローでアクセストークンを使う

GitHub Actionsのワークフローを組むときの大事なポイント:

  • actions/create-github-app-tokenでGitHub Apps Installation Access Tokenを取得する
  • それをdocker/build-push-actionsecretsとして渡す

以下がワークフローの全容:

push-container-image.yml
name: Push container image

on:
  push:
    branches:
      - main
    tags:
      - v*.*.*

env:
  REGISTRY: ghcr.io
  # "ghcr.io/your-org/your-repo" のような値が代入される
  IMAGE_PATH: ghcr.io/${{ github.repository }}

jobs:
  push-to-registry:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read

    steps:
      - name: Checkout repository
        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

      # GitHub App Installation Access Tokenを取得するステップ
      - name: Create GitHub App Token
        uses: actions/create-github-app-token@bf559f85448f9380bcfa2899dbdc01eb5b37be3a # v3.0.0-beta.2
        # 別ステップで参照できるようにidを宣言する
        id: app-token
        with:
          # 環境変数からApp IDをセット
          app-id: ${{ secrets.GITHUB_ACTIONS_APP_ID }}
          # 環境変数から秘密鍵をセット
          private-key: ${{ secrets.GITHUB_ACTIONS_APP_PRIVATE_KEY }}
          # 組織内の別のリポジトリを参照する場合は owner の指定が必須
          owner: ${{ github.repository_owner }}

      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1

      - name: Login to Container Registry
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract Docker metadata
        uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
        id: meta
        with:
          images: |
            ${{ env.IMAGE_PATH }}
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
            type=raw,value=edge,enable={{is_default_branch}}

      - name: Build and push Docker images
        uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
        with:
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          annotations: ${{ steps.meta.outputs.annotations }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64
          # Dockerfileのtype=secret,id=GH_TOKENにGitHub App Installation Access Tokenを渡す指定
          secrets: |
            GH_TOKEN=${{ steps.app-token.outputs.token }}

GITHUB_TOKENはなぜ使えない?

GITHUB_TOKENは自リポジトリのみが参照可能です。プライベートな他リポジトリを参照する権限がついていないので、GITHUB_TOKENは役に立ちません。

トラブルシューティング

go mod downloadで「Invalid username or token」

Invalid username or token. Password authentication is not supported for Git operations.

このようなエラーがdocker/build-push-actionで起きたのなら、GH_TOKENをDockerfileに上手く渡せていないかもしれない。次のステップをDockerfileに足し、本当にGH_TOKENが渡ってきているか確かめよう。

RUN --mount=type=secret,id=GH_TOKEN,env=GH_TOKEN \
    test -n "${GH_TOKEN}" && echo "GH_TOKEN=present" || echo "GH_TOKEN=empty"; \
    echo "GH_TOKEN length: $(echo -n "${GH_TOKEN}" | wc -c)"

go mod downloadで「Repository not found」

これがローカルでは起きずに、GitHub Actionsだけで起きるなら、考えられる原因は次のとおり:

  • create-github-app-tokenownerが指定されていない。ownerを指定しないと、発行されるトークンには、GitHub Actionsが実行されたリポジトリのアクセス権限しかつかない。
  • create-github-app-tokenrepositoriesに対象リポジトリがリストアップされていない。repositories自体がない場合は、GitHub Appをインストールした全リポジトリが自動的にセットされるので、repositoriesを一旦コメントアウトするのもあり。
- name: Create GitHub App Token
  uses: actions/create-github-app-token@bf559f85448f9380bcfa2899dbdc01eb5b37be3a
  id: app-token
  with:
    app-id: ${{ secrets.GITHUB_ACTIONS_APP_ID }}
    private-key: ${{ secrets.GITHUB_ACTIONS_APP_PRIVATE_KEY }}
    owner: your-org # ここが指定されていない
    repositories: |
      repo1
      repo2
    # ↑ここにリストアップされていない
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?