🛠

Cloud BuildでPATを使ったgitサブモジュールの利用方法

2023/04/07に公開

要約

クラウドエースの北野です。

Google CloudのCloud BuildでGitHubのプライベートリポジトリをサブモジュールとして使う方法を紹介します。本記事では、プライベートリポジトリの認証にGitHubのPersonal Access Token (PAT)を使います。

Google CloudでのPATの管理方法、Cloud Buildで認証情報の持たせ方と認証情報の内容は以下となります。

  • PATの管理方法: Secret Managerで管理
  • Cloud Buildでの認証方法の持たせ方: ~/.git-credentialsに定義

~/.git-credentialsの情報を読み込ませるコマンドは以下の通りです。

git config --global credential.helper store --file

Cloud Buildでgitサブモジュールを取得するビルド構成ファイルは以下の通りです。

steps:
  - id: "create git-credentials"
    name: 'gcr.io/cloud-builders/git'
    secretEnv:
      - 'PAT'
    entrypoint: bash
    args:
      - -c
      - |
        cat << EOF > ~/.git-credentials
        https://<GitHub アカウント名>:$$PAT@github.com/<GitHub アカウント名>/<サブモジュールリポジトリ>
        EOF

  - id: "git credential config"
    name: 'gcr.io/cloud-builders/git'
    entrypoint: bash
    args:
      - -c
      - |
        git config --global credential.helper store --file

  - id: 'git submodule update'
    name: 'gcr.io/cloud-builders/git'
    entrypoint: bash
    args:
      - -c
      - |
        git submodule update --init --recursive

availableSecrets:
  secretManager:
    - versionName: projects/${PROJECT_ID}/secrets/<Secret Manager名>/versions/latest
      env: 'PAT'

背景

アプリケーション開発などにおいて、他のリポジトリを参照させたいことがあると思います。参照先がパブリックリポジトリであれば、認証情報を設定せずに参照させられます。しかし、参照先が社内のプライベートリポジトリなどであれば、認証情報が必要となります。ローカルでの開発の場合であれば、開発者の認証情報でアクセスできますが、CI/CDツールでビルドするとき、CI/CDエージェントに認証情報を持たせる必要があります。

そこで、本記事では、Google CloudのCI/CDプラットフォームであるCloud BuildでGitのサブモジュールにプライベートリポジトリを使う方法を紹介します。

GitHubの認証方法

GitHubの認証は、以下の3つの方法があります。

  • 2要素認証のユーザー名とパスワード
  • SSHキー
  • Personal access token (PAT)

2要素認証は認証時にワンタイムパスワードを求められるため、CIツールの認証に活用するのは適しません。また、SSHキーはリポジトリごとに認証させるSSH鍵を分けたりするなど細かな認証・認可の制御ができません。PATはリポジトリごとに発行させたり、PATごとにリポジトリに対して操作できる機能を制限するなど、認証と認可を細かに設定できます。そのため、PATを使うとより安全にプライベートリポジトリへアクセスさせることできます。

本記事では、Cloud BuildのビルドステップからPATを使い認証させる方法を紹介します。

秘匿情報の管理とCloud Buildによるアクセス

PATの情報は認証情報であり、漏洩すると意図しないエンドポイントからのアクセスが可能となるので、セキュアに管理する必要があります。ここでは、Google CloudでPATの情報などの秘匿情報をSecret Managerによりセキュアに管理する方法と、Secret Managerで管理されているデータをCloud Buildから取得する方法を紹介します。

Google Cloudでの秘匿情報の管理

Secret Managerは、機密データを保存するための安全で便利なストレージです。IAMによりデータへのアクセスを制限し、アクセスの監査ができるマネージドサービスです。

ここでは、以下の内容でSecret Managerを作成します。

  • シークレット名: sample
  • シークレット値: private repository git submodule
  • レプリケーションポリシー: このシークレットのロケーションを手動で管理する
    • ロケーション: 東京 (asia-northeast1)
  • その他の設定: デフォルト

PAT作成
シークレット値が、Secret Managerで管理される秘匿情報となります。
Secret Managerに保存されたシークレット値は、以下のコマンドで取得できます。

gcloud secrets versions access latest --secret="sample"

上記のlatestは、Secret Managerのsampleのバージョンの値です。latestは最新のバージョンの指定となっています。特定のバージョンへのアクセスはバージョン数を指定してください。Secret Managerに格納されたデータへののアクセスには Secret Managerのシークレットアクセサー(roles/secretmanager.secretAccessor) のIAMロールが必要となります。当該IAMロールは、個々のSecret Managerに付与ができます。

参考文献: Secret Manager IAMを使用したアクセス制限

Cloud BuildのビルドからSecret Managerのデータへアクセスする方法

Cloud BuildのビルドからSecret Managerのデータを取得する方法を紹介します。
アクセスには、以下が必要になります。

  • 実行するビルド環境のサービスアカウントへの roles/secretmanager.secretAccessor ロールの付与
  • ビルド構成ファイルにデータを取得するSecret Managerの情報の定義

ビルド環境のサービスアカウントはトリガー作成時にサービスアカウントを指定しないと、<プロジェクト ナンバー>@cloudbuild.gserviceaccount.com となります。本記事では、当該Cloud Buildのデフォルトのサービスアカウントを使うこととします。
Cloud コンソールのIAMのページからCloud Buildのデフォルトサービスアカウントにプロジェクレベルで Secret Managerのシークレットアクセサー の権限を付与します。

Cloud Buildのサービスアカウント

ビルド構成ファイルでの参照するSecret Managerの定義は、stepsの後にavailableSecrets.secretManager フィールドを記載します。
availableSecrets.secretManagerには、以下の2つを定義します。

  • versionName: Secret Manager名と参照するデータのバージョン (例: projects/${PROJECT_ID}/secrets/<Secret Manager名>/versions/<バージョン数>)
  • env: ビルドで呼び出すときの変数名

ステップでの参照は、secretEnvフィールドに参照するSecret Managerのenvの値を定義し、args内で$$を先頭に付けて呼び出します。

以下は、先程作成したSecret Managerのsampleの値をechoコマンドで表示するビルド構成ファイルです。Secret Managerのsampleのバージョン数は、latestとして最新のバージョンを指定しています。

steps:
  - name: 'ubuntu'
    entrypoint: 'bash'
    args:
      - -c
      - |
        echo $$SECRET
    secretEnv:
      - SECRET

availableSecrets:
  secretManager:
    - versionName: projects/$PROJECT_ID/secrets/sample/versions/latest
      env: SECRET

上記ファイルを任意のパスにcloudbuild.yamlという名前で保存し、下記コマンドでビルドを実行させます。

gcloud builds submit --region=asia-northeast1 --config cloudbuild.yaml --no-source

当該コマンドで実行した結果をCloud コンソールから結果を確認すると、以下のようにSecret Managerに登録したシークレット値(private repository git submoduleの文字列)が表示されます。

CloudBuildのビルド実行結果

参照URL: Cloud BuildでのSecret Managerのシークレットを使用

Cloud Buildでgitサブモジュールを使う方法

認証にPATを使いCloud Buildでプライベートリポジトリをサブモジュールとして使う方法を紹介します。本記事では、2022年10月18日に公開されたFine-grained personal access tokensを使います。 2023年4月時点では、Betaとなっているので本番運用などについては、考慮のうえ活用ください。また、PATは決して、GitHubのリポジトリに登録しないでください。

まず、PATを発行をします。PATの発行は、GitHubの個人設定画面の Developer settings から作成をおこないます。

個人設定

Personal access tokensFine-grained tokensGenerate new token からPATを作成します。

PAT作成

ユーザー認証が求められるので、ユーザーの認証情報を入力します。PATの作成画面では必要な情報を入力します。このときRepository accessのOnly select repositoriesを選択し、アクセスするプライベートリポジトリを選択します。

PAT作成

また、Repository PermissionsContentsのAccessをRead-onlyとします。(ContentsRead-onlyとすると、自動的にMetadataRead-onlyとなります。)すると、PATにプライベートリポジトリのコンテンツへのリード権限のみ付与されるので、PATが漏洩したとしても被害を最小限にできます。

PAT作成

作成が完了すると、以下のようにPATが表示されます。

PATリスト

得られたPATをSecret Managerに登録し、Cloud Buildから呼び出します。ここでは、以下の内容でSecret Managerを作成し、PATを登録します。

  • シークレット名: github-pat
  • シークレット値: GitHubで生成されたPAT
  • レプリケーションポリシー: このシークレットのロケーションを手動で管理する
    • ロケーション: 東京 (asia-northeast1)
  • その他の設定: デフォルト

Cloud Buildからプライベートリポジトリへの認証は、gitの認証情報の保存機能によりおこないます。認証の方法は以下の通りです。

  1. ~/.git-credentials の認証ファイルの作成
  2. git config コマンドで認証情報の読み込み

~/.git-credentialsの内容は、https://<GitHub Account>:<PAT>@github.com/<GitHub アカウント名>/<Repository>となります。当該ファイルはCloud Buildのビルド内で生成し、リポジトリで管理しません。

~/.git-credentialsの認証情報を読み込むコマンドは以下となります。

git config --global credential.helper store --file

ここからは以下の2つのプライベートリポジトリを使い、Cloud Buildからプライベートリポジトリをサブモジュールで呼び出します。

  • サブモジュールのリポジトリ: private-submodule-repo
  • サブモジュールを呼び出すリポジトリ(Cloud Buildから実行させるリポジトリ): call-git-submodule

private-submodule-repoのリポジトリ構造は、以下の通りです。

.
├── README.md
└── src
    └── hello.sh

hello.shの中身は、以下となっています。

#!/bin/bash

echo "このスクリプトはサブモジュールで参照しているプライベートリポジトリです"

call-git-submoduleのリポジトリ構造は以下の通りです。

.
├── README.md
├── cloudbuild
│   └── cloudbuild.yaml
├── src
│   └── main.sh
└── submodule
    ├── README.md
    └── src
         └── hello.sh

submoduleディレクトリ以下が、gitサブモジュールで、private-submodule-repo を参照しています。参照させるコマンドは以下となっています。

git submodule add https://github.com/<GitHub アカウント名>/private-submodule-repo.git submodule

cloudbuildディレクトリ配下のcloudbuild.yamlが、Cloud Buildで実行するビルドを定義しているビルド構成ファイルとなっています。cloudbuild.yamlの中身は、以下のようになっています。

steps:
  - id: "create git-credentials"
    name: 'gcr.io/cloud-builders/git'
    entrypoint: bash
    args:
      - -c
      - |
        cat << EOF > ~/.git-credentials
        https://<GitHub アカウント名>:$$PAT@github.com/<GitHub アカウント名>/private-submodule-repo.git
        EOF

  - id: "git credential config"
    name: 'gcr.io/cloud-builders/git'
    entrypoint: bash
    args:
      - -c
      - |
        git config --global credential.helper store --file

  - id: 'git submodule update'
    name: 'gcr.io/cloud-builders/git'
    entrypoint: bash
    args:
      - -c
      - |
        git submodule update --init --recursive
        
  - id: "bash submodule"
    name: 'ubuntu'
    entrypoint: bash
    dir: src
    args:
      - -c
      - |
        bash main.sh

availableSecrets:
  secretManager:
    - versionName: projects/${PROJECT_ID}/secrets/github-pat/versions/latest
      env: 'PAT'

ビルドのステップで実行する src/main.shの中身は、以下の通りでサブモジュールのhello.shを実行するスクリプトとなっています。

#!/bin/bash

bash ../submodule/src/hello.sh

call-git-submoduleのリポジトリを接続し、Cloud Buildトリガーを以下の設定で作成します。

  • トリガー名: private-submodule-sample
  • リージョン: 東京 (asia-northeast1)
  • イベント: なし
  • 呼び出し: 手動呼び出し
  • ソース: 第1世代
  • リポジトリ: call-git-submodule(GitHubアプリ)
  • リビジョン: ^main$
  • 形式: Cloud Build構成ファイル
  • ロケーション: リポジトリ
  • Cloud Build構成ファイルの場所: cloudbuild/cloudbuild.yaml
  • サービスアカウント: 指定なし (デフォルトサービスアカウント)

PAT作成

実際に手動トリガーの一覧画面からprivate-submodule-sample実行ボタンからビルドを実行させます。

PATのトリガー実行

実行結果を確認すると、プライベートリポジトリを参照できていることが分かります。

PATのトリガー結果

まとめ

当該記事では、Cloud BuildでGitHubのプライベートリポジトリの参照をPATによりおこなう方法を紹介しました。プライベートリポジトリの参照はgitのサブモジュールを一例として紹介しましたが、サブモジュール以外の方法においても同様の方法で参照が可能となるので、参考にしてください。

Discussion