OIDC を利用した GitLab CI/CD から Google Cloud への認証設定

に公開

はじめに

こんにちは!
株式会社サイエンスアーツでインフラを担当している相川と申します!

今回は、OIDCを利用してGitLab CI/CDからGoogle Cloudへの認証を行う設定についてご紹介します。

記事で触れないこと

OIDCやGoogle CloudのWorkload Identity連携という技術自体の詳細については触れません。

前提条件

今回はCLIで設定していきます。
Google Cloudにログイン出来ているか確認します。

gcloud config configurations list

IS_ACTIVEがTrueである行が表示されれば問題ありません。
未ログインの場合は、gcloud initgcloud auth loginなどでログインします。

ログイン方法の詳細はこちら
https://docs.cloud.google.com/docs/authentication/gcloud?hl=ja

gcloud CLIでの接続先プロジェクトの追加や変更などはこちら
https://cloud.google.com/sdk/docs/configurations?hl=ja

手順

  1. APIの有効化
  2. シェル変数の設定
  3. Workload Identity Poolの作成
  4. Workload Identity Providerの作成
  5. サービスアカウントの作成・権限付与
  6. サービスアカウントの権限借用
  7. GitLab CI/CDの構成

1. APIの有効化

Google CloudではAPIを利用する際にあらかじめ有効化する必要があるため、下記のコマンドを実行します。

gcloud services enable iam.googleapis.com \
                       iamcredentials.googleapis.com \
                       cloudresourcemanager.googleapis.com \
                       servicenetworking.googleapis.com \
                       sts.googleapis.com

有効化できたか確認。

gcloud services list

2. シェル変数の設定

あとあとコマンドの引数などで使用するため、先にシェル変数として設定しておきます。

GITLAB_PROJECT_ID(GitLabのプロジェクトID)はプロジェクトのトップページのケバブメニューから確認できます。
その他の変数については、命名をお願いします。

GITLAB_PROJECT_ID=
WI_POOL_ID=
WI_POOL_DISPLAY_NAME=
WI_PROVIDER_ID=
SERVICE_ACCOUNT_ID=

下記の変数はコマンドで設定するか、決まった値を入れます。

GOOGLE_CLOUD_PROJECT_ID=$(gcloud config get-value project)
GOOGLE_CLOUD_PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT_ID --format="value(projectNumber)")
WI_PROVIDER_ISSUER_URI='https://gitlab.com'
WI_PROVIDER_ATTRIBUTE_MAPPING='google.subject=assertion.sub,attribute.project_id=assertion.project_id'
SERVICE_ACCOUNT_EMAIL=${SERVICE_ACCOUNT_ID}@${GOOGLE_CLOUD_PROJECT_ID}.iam.gserviceaccount.com

WI_PROVIDER_ATTRIBUTE_MAPPINGについては、状況により値を変えることがあるので、後ほど解説します。

3. Workload Identity Poolの作成

gcloud iam workload-identity-pools create $WI_POOL_ID \
    --location="global" \
    --display-name=$WI_POOL_DISPLAY_NAME

4. Workload Identity Providerの作成

gcloud iam workload-identity-pools providers create-oidc $WI_PROVIDER_ID \
    --location="global" \
    --workload-identity-pool=$WI_POOL_ID \
    --issuer-uri=$WI_PROVIDER_ISSUER_URI \
    --attribute-mapping=$WI_PROVIDER_ATTRIBUTE_MAPPING \
    --attribute-condition="attribute.project_id=='${GITLAB_PROJECT_ID}'"

先ほど補足したWI_PROVIDER_ATTRIBUTE_MAPPINGについて説明します。OIDCの仕組みの一部で、

  • Issuer(発行者、今回はGitLab)側がCI/CD Jobを認証し、ID Tokenを発行
  • Audience(受け取り側、今回はGoogle Cloud)がID Tokenを受け取り、ID Token内のclaims(クレーム)を確認。

ということが行われています。

ここでattribute mapping(属性マッピング)により、GitLabのプロジェクトIDをAudience側の属性にマッピングし、attribute condition(属性条件)により値が正しいかどうかでアクセス制御を行うことが出来ます。

GitLabのプロジェクトIDなどのkeyとvalueのセットはID Token内のcustom claims(カスタムクレーム)に含まれます。

カスタムクレームの一覧は下記になります。
https://docs.gitlab.com/ci/secrets/id_token_authentication/#token-payload

これらの中から属性条件として利用したい項目を属性マッピングによりAudience側に渡します。

今回は、プロジェクトIDを属性条件として利用しましたが、各々利用したい項目によりattribute mappingとattribute conditionの値を変えてください。

5. サービスアカウントの作成・権限付与

サービスアカウントの作成

gcloud iam service-accounts create $SERVICE_ACCOUNT_ID \
  --display-name="$SERVICE_ACCOUNT_ID"

権限付与
roles/iam.serviceAccountTokenCreatorは必須。

gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT_ID \
  --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
  --role="roles/iam.serviceAccountTokenCreator"

今回は、CI/CDでTerraformを実行し、監視設定を行うため、それらに関係するリソースを扱える権限も付与する。

gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT_ID \
  --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
  --role="roles/monitoring.editor"

6. サービスアカウントの権限借用

gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT_EMAIL \
    --role=roles/iam.workloadIdentityUser \
    --member="principalSet://iam.googleapis.com/projects/${GOOGLE_CLOUD_PROJECT_NUMBER}/locations/global/workloadIdentityPools/${WI_POOL_ID}/attribute.project_id/${GITLAB_PROJECT_ID}"

workloadIdentityUserのロールを付与されたプリンシパルは、特定のサービスアカウントを借用(インパーソネーション)して、そのサービスアカウントに付与された権限に基づき、Google Cloudのリソースにアクセスできるようになります。

また、GitLabのプロジェクトIDをGoogle Cloud側の属性としてマッピング出来ているため、principalSetの指定で、その属性に基づく集合に、このポリシーを紐づけ出来ています。
属性マッピングのマッピング対象を変更する場合、こちらも併せて変更してください。

7. GitLab CI/CDの構成

GitLab CI/CDに利用する.gitlab-ci.ymlのOIDC周りのサンプルは下記となります。

job:
  variables:
    WORKLOAD_IDENTITY_PROJECT_NUMBER: PROJECT_NUMBER
    WORKLOAD_IDENTITY_POOL: POOL_ID
    WORKLOAD_IDENTITY_PROVIDER: PROVIDER_ID
    SERVICE_ACCOUNT: SERVICE_ACCOUNT_EMAIL
    GOOGLE_APPLICATION_CREDENTIALS: $CI_BUILDS_DIR/.workload_identity.wlconfig

  id_tokens:
    WORKLOAD_IDENTITY_TOKEN:
      aud: https://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID

  script:
    - |-
      echo $WORKLOAD_IDENTITY_TOKEN > $CI_BUILDS_DIR/.workload_identity.jwt
      cat << EOF > $GOOGLE_APPLICATION_CREDENTIALS
      {
        "type": "external_account",
        "audience": "//iam.googleapis.com/projects/$WORKLOAD_IDENTITY_PROJECT_NUMBER/locations/global/workloadIdentityPools/$WORKLOAD_IDENTITY_POOL/providers/$WORKLOAD_IDENTITY_PROVIDER",
        "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
        "token_url": "https://sts.googleapis.com/v1/token",
        "credential_source": {
          "file": "$CI_BUILDS_DIR/.workload_identity.jwt"
        },
        "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/$SERVICE_ACCOUNT:generateAccessToken"
      }
      EOF

下記については各自の値に修正してください。

  • job:variablesに定義された5つの変数うち、上4つ
  • id_tokens.<name>.audフィールド内のPROJECT_NUMBER,POOL_ID,PROVIDER_ID

注意点なのですが、GitLab CIの仕様上、id_tokens:はジョブ変数の展開より前に評価されるセクションであるため、${VARIABLE}などによりvariables:の値を埋め込むことはできません。

このサンプルを元に、各自Terraformやgcloud CLIを実行するジョブの前に、OIDCによる認証をする形となります。

私の場合は、このジョブの先頭に.(ドット)をつけhidden jobとして扱い、terraform planやapplyを実行するジョブでextends:により継承して利用しています。
また、この場合は、hidden jobのscript:が継承先に上書きされてしまうため、before_script:に書き換えてください。

さいごに

今回は、OIDCを利用してGitLab CI/CDからGoogle Cloudへの認証を行う設定について紹介しました。
自分で設定した際に結構ハマったため、同じような設定をされる方の参考になれば幸いです。

参考

https://cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines?hl=ja

Discussion