Cloud Build で シークレット マネージャー を使用して GitHub プライベートリポジトリに接続する

背景

Cloud Build はGCP上でビルド処理を実行するためのサービスです。 主に CI/CD パイプラインを構築するために利用されます。

公式ドキュメント “限定公開 GitHub リポジトリへのアクセス” ではこのCloud BuildでGitHubのプライベートリポジトリにアクセスする方法が紹介されています。その内容は、あらかじめGitHubに接続するためのSSH秘密鍵を暗号化しておき、ビルドプロセスの中で Cloud KMS を使って秘密鍵を復号してSSH認証に使用するというものです。

図1: 限定公開 GitHub リポジトリへのアクセス

この例は手動ビルドを実行する場合を想定しており、 ビルドトリガーを使う場合 は秘密鍵を使わず自動的にプライベートリポジトリにアクセスできるとしています。

図2: ビルドトリガーを使う場合

しかし、例えば

  • ビルド構成設定とアプリケーションのソースコードを別のリポジトリで管理したい場合
  • ビルド時に複数のGitHubリポジトリからソースを取得したい場合

など、自動トリガーによって開始されたビルド中に他のGitHubプライベートリポジトリへアクセスしたい場合には、やはりSSH秘密鍵等の認証情報が必要になります。

このとき上の Cloud KMS による方法を使おうとすると、ソースに暗号化されたSSH秘密鍵を含める必要があります。しかし、暗号化されているとはいえソースに秘密鍵をコミットする運用は避けたいところです。

そこで、本記事では Cloud KMS に替えて シークレット マネージャー を使用して秘密鍵を取得し、それを使ってSSH認証を行うことを試みます。

図3: シークレット マネージャーを使用する

シークレット マネージャーは 2020年3月11日に正式版がリリース されたばかりの新しいサービスで、秘密情報専用の管理機構を備えています。 これを利用すると任意の秘密情報を保存した上で Cloud IAM によるアクセス制御を行うことができるようになります。Cloud Buildの実行時にはIAMのサービスアカウントが与えられるので、そのサービスアカウントに対してアクセスを許可することによってビルド中に秘密情報を取得できるようになります。


事前準備

GitHub と GCP

  • 2つの GitHub リポジトリ を用意します
    • ソースリポジトリ username/private-source-repo
      • Cloud Build からアクセスしたい非公開リポジトリです。内容は何でも構いません。
    • ビルド構成管理リポジトリ username/cloudbuild-config-repo
      • Cloud Build のビルド構成ファイル等を管理するためのリポジトリです。
  • GCPプロジェクト を1つ用意します(参考
    • プロジェクト名は任意です。ここでは projectname とします。
    • このプロジェクトで Secret Manager API を有効化しておきます。
  • ローカルマシンに gcloud コマンドラインツール をインストールしておきます

GCPプロジェクト名やGitHubユーザー名・リポジトリ名は任意です。 以下の例で必要な箇所については適当な名前で読み替えてください。

gcloud の設定

ローカルマシンで以下のコマンドを実行し、ユーザー認証を行います

gcloud auth login

対象プロジェクトを projectname に設定します

gcloud config set project projectname

デプロイキーの作成

以下の例ではGitHubプライベートリポジトリへアクセスする際に デプロイキー による認証を利用します。 なお本記事の内容はデプロイキーの代わりに適当な マシンユーザー アカウントのSSH鍵や Personal Access Token でアクセスする場合にも同様に適用することができます。

次のコマンドで秘密鍵 github_deploy_key / 公開鍵 github_deploy_key.pub ペアを生成します:

ssh-keygen -f github_deploy_key

鍵ペアを作成したら、ブラウザでGitHubリポジトリ username/private-source-repo の “Settings” ページを開き、公開鍵 github_deploy_key.pubDeploy Key として登録しておきます。

これで秘密鍵 github_deploy_key による認証でプライベートリポジトリ username/private-source-repo にアクセスできるようになります。


秘密鍵をシークレットとして保存

次のコマンドを実行して、作業ディレクトリにある秘密鍵ファイル github_deploy_key の内容を シークレット マネージャー に保存します:

gcloud secrets create github_deploy_key --data-file=github_deploy_key

これによって github_deploy_key という名前のシークレットが作成されます。

次のようにして保存された秘密鍵にアクセスできることを確認します:

gcloud secrets versions access --secret=github_deploy_key latest

シークレットの作成はGCPコンソールのウェブUI上でも可能ですが、Cloud Build では gcloud を使ってアクセスすることになるので、あらかじめローカル環境で挙動を確認しておいたほうが良いでしょう。


アクセス権の設定

Cloud Build から保存されたシークレットの内容にアクセスできるように設定します。

Cloud IAM でサービスアカウント [PROJECT_NUMBER]@cloudbuild.gserviceaccount.com に対して Secret Manager のシークレット アクセサー の役割を与えます(参考: “サービス アカウントへの役割の付与”)。このとき、 [PROJECT_NUMBER] はプロジェクト projectname のプロジェクト番号で置き換えます。 プロジェクト番号はGCPコンソールのダッシュボードで確認します(参考)。

このサービスアカウント [PROJECT_NUMBER]@cloudbuild.gserviceaccount.com は Cloud Build を実行する際に使用される Google 管理サービス アカウント です。


ビルド構成管理リポジトリの作成

以下、ビルド構成管理リポジトリ username/cloudbuild-config-repo に必要な内容を用意していきます。 このリポジトリには known_hostscloudbuild.yaml の2つのファイルを作成します。

known_hosts ファイルを作成または更新

次のコマンドで known_hosts ファイルを作成します:

ssh-keyscan -t rsa github.com > known_hosts

このファイルは github.com を既知のSSHホストとして確認するために使用されます。

ビルド構成ファイルを作成

Cloud Build のビルド構成ファイル cloudbuild.yaml を以下のような内容で作成します:

steps:
# Secret Manager からデプロイキーをダウンロードする
- name: gcr.io/cloud-builders/gcloud
  entrypoint: bash
  args:
  - -c
  - gcloud secrets versions access --secret=github_deploy_key latest > /root/.ssh/id_rsa
  volumes:
  - name: ssh
    path: /root/.ssh
# GitHubとのSSH接続のセットアップを行う
- name: gcr.io/cloud-builders/git
  entrypoint: bash
  args:
  - -c
  - |
    chmod 600 /root/.ssh/id_rsa
    cat <<EOF >/root/.ssh/config
    Hostname github.com
    IdentityFile /root/.ssh/id_rsa
    EOF
    mv known_hosts /root/.ssh/known_hosts
  volumes:
  - name: ssh
    path: /root/.ssh
# GitHubリポジトリ username/private-source-repo を /src/private-source-repo にクローンする
- name: gcr.io/cloud-builders/git
  args:
  - clone
  - git@github.com:username/private-source-repo
  - /src/private-source-repo
  volumes:
  - name: ssh
    path: /root/.ssh
  - name: src
    path: /src
#
# 以下、クローンしたソースを使用するステップが続く...
#
# 例として ls してみる
- name: alpine
  args:
  - ls
  - /src/private-source-repo
  volumes:
  - name: src
    path: /src

ポイントは最初のステップです:

# Secret Manager からデプロイキーをダウンロードする
- name: gcr.io/cloud-builders/gcloud
  entrypoint: bash
  args:
  - -c
  - gcloud secrets versions access --secret=github_deploy_key latest > /root/.ssh/id_rsa
  volumes:
  - name: ssh
    path: /root/.ssh

このステップではシークレット マネージャーに保存されたシークレット github_deploy_key にアクセスし、その内容を /root/.ssh/id_rsa に書き込みます。 ここで gcloud コンテナの ENTRYPOINTbash に変更することでリダイレクト > による書き込みを可能にしています。 結局、このステップでは次のコマンドが実行されることになります:

bash -c 'gcloud secrets versions access --secret=github_deploy_key latest > /root/.ssh/id_rsa'

また、作業ディレクトリ (/workspace) 以外の場所にファイルを作成するときは volumes を指定して永続化することも必要です(この例では /root/.ssh/src)。

以上のように known_hostscloudbuild.yaml を作成したら、リポジトリ username/cloudbuild-config-repo にコミットしておきます。


ビルドトリガーの設定

GCPコンソールの “ビルドトリガー” ページ を開き、次のような設定でビルドトリガーを作成します:

  • GitHubリポジトリ username/cloudbuild-config-repo を Cloud Source Repositories にミラーリングしてビルドソースとする
  • master ブランチへのプッシュなど適当なトリガー条件を設定
  • ビルド構成ファイルとして cloudbuild.yaml を使用する

ビルドトリガーの作成手順はドキュメント “ビルドトリガーを使用したビルドの自動化” に書かれているとおりです。


テスト

リポジトリ username/cloudbuild-config-repo に適当なコミットを push するか、手動でトリガーを実行するとビルドが開始されます。

実行されたビルドの状態やログは “ビルド履歴” のページ から確認することができます。

この例ではデモとして ls コマンドで /src/private-source-repo ディレクトリ内のファイル名をログに出力しています。思惑通りソースリポジトリがクローンできていればその内容がログ出力に反映されるはずです。


まとめ

Cloud Build の自動トリガーされたビルドで、ビルドソース以外のGitHubプライベートリポジトリにアクセスするため、以下のことを行いました:

  • シークレット マネージャー にGitHub接続用のSSH秘密鍵を保存しました
  • Cloud Build から シークレット マネージャー にアクセスできるよう Cloud IAM の設定を行いました
  • Cloud Build のビルドプロセス内で シークレット マネージャー にアクセスしてSSH秘密鍵をダウンロードしました
  • ダウンロードしたSSH秘密鍵を使って Cloud Build から GitHub に接続しました

この例では認証情報としてSSH秘密鍵を使ってGitHubのプライベートリポジトリにアクセスしましたが、 他にもAWSアクセスキーを使って非公開S3バケットにアクセスしたり、パスワードを使ってデータベースにアクセスしたりと、様々なケースに応用できるはずです。


参考