GKE クラスタ内のワークロードから Google Cloud APIs にアクセスする(Workload Identity)

記事タイトルとURLをコピーする

G-gen の佐々木です。当記事では、Google Kubernetes Engine (以下、GKE) のクラスタにデプロイした Pod から Google Cloud APIs にアクセスする方法を解説します。

GKE の概要

GKE は、Google Cloud のインフラストラクチャ上に構築された マネージドな Kubernetes クラスタ を利用することができるサービスです。
サービスの詳細は以下の記事で解説しています。

blog.g-gen.co.jp

GKE クラスタ内の Pod から Google Cloud APIs にアクセスする方法

GKE のワークロードでクラスタ外の Cloud Storage バケットを使用する場合など、Pod から Google Cloud APIs にアクセスするためには、Google Cloud の IAM サービスアカウントを使用します。
Pod で IAM サービスアカウントを使用する方法については以下の 3 パターンがあります。

  • ノードに紐付いたサービスアカウントを使用する
  • サービスアカウントのキーを Kubernetes Secret として GKE クラスタに登録する
  • Workload Identity を使用する(推奨)

※ 限定公開クラスタの場合、Google Cloud APIs にアクセスするには 限定公開の Google アクセス(デフォルトで有効)もしくは Cloud NAT が必要となります。

ノードに紐付いたサービスアカウントを使用する

利用可能なクラスタモード
Standard クラスタのみ

GKE のノードは Compute Engine VM で構成されており、ノードプールに設定した IAM サービスアカウントが、各ノード VM に紐付けられます。

ノードに紐付いたサービスアカウントは、Pod に対して何も設定していなくても、Google Cloud APIs にリクエストを送信する際に自動的に使用されます。

試しに、gcloud コマンドがプリインストールされたコンテナイメージを使用して、以下の Pod を GKE クラスタにデプロイします。

apiVersion: v1
kind: Pod
metadata:
  name: gcloud-pod
spec:
  containers:
  - name: cloud-sdk
    image: google/cloud-sdk:slim
    command: ["sleep", "infinity"]

Pod の作成後、gcloud auth list コマンドで Pod から gcloud コマンドを実行する際に使用されるクレデンシャルを確認します。

# Pod が使用する IAM サービスアカウントを確認する
$ kubectl exec -it gcloud-pod -- gcloud auth list
                  Credentialed Accounts
ACTIVE  ACCOUNT
*       xxxxxxxxxxxx-compute@developer.gserviceaccount.com

今回使用した GKE クラスタのノードには Compute Engine のデフォルトのサービスアカウント が紐付いているため、Pod から Google Cloud APIs へのリクエストには {プロジェクトID}-compute@developer.gserviceaccount.com のサービスアカウントが使用されています。

ノードプールを作成する際にサービスアカウントを指定しなかった場合は、Compute Engine のデフォルトのサービスアカウントがノードに紐付くようになっています。
Compute Engine のデフォルトのサービスアカウントは IAM 基本ロールである 編集者(roles/editor)のロールが付与されているため、クラスタ内で起動する Pod がかなり過剰な権限を持ってしまいます。

また、このようにノードに紐付いたサービスアカウントを使用する場合、そのノードに割り当てられた Pod は全て同じサービスアカウントを使用して Google Cloud APIs にリクエストを送信します。
したがって、1 つの Pod のために様々な権限をサービスアカウントに付与する必要がある場合などには、その他の Pod が 最小権限の原則 に反する運用になってしまいます。

Pod からノードに紐付いたサービスアカウントを使用する場合

サービスアカウントのキーを Kubernetes Secret として GKE クラスタに登録する

利用可能なクラスタモード
Standard クラスタ・Autopilot クラスタ

サービスアカウントのキーをエクスポートし、Kubernetes の Secret としてクラスタに登録することで、Pod から Secret に保存されたサービスアカウントキーを使用できます。

サービスアカウントキーをエクスポート したあと、以下のコマンドを使用して GKE クラスタに Secret リソースとして登録します。

# サービスアカウントキーを保存する Secret を作成する
$ kubectl create secret generic gsa-key --from-file=key.json={サービスアカウントキーのファイルパス}

サービスアカウントキーが保存された Secret リソースを Pod にマウントし、環境変数 GOOGLE_APPLICATION_CREDENTIALS でサービスアカウントキーを参照できるように設定します。
クライアントライブラリから Google Cloud APIs にアクセスする際、環境変数 GOOGLE_APPLICATION_CREDENTIALS に設定されたサービスアカウントキーが自動的に使用されます(参考)。

apiVersion: v1
kind: Pod
metadata:
  name: gcloud-pod-gsa-key
spec:
  volumes:
  - name: google-cloud-key
    secret:
      secretName: gsa-key  # 登録した Secret の名前
  containers:
  - name: cloud-sdk
    image: google/cloud-sdk:slim
    command: ["sleep", "infinity"]
    volumeMounts:
      - name: google-cloud-key
        mountPath: /var/secrets/google
    env:
    - name: GOOGLE_APPLICATION_CREDENTIALS  # サービスアカウントキーを環境変数に指定
      value: /var/secrets/google/key.json

この方法では、Pod ごとに別々のサービスアカウントキーを使用することで、最小権限の原則を遵守した運用が可能です。
しかし、サービスアカウントキーが増えるたびに、漏洩対策やライフサイクル管理が煩雑になっていきます。

サービスアカウントのキーを使用する場合

Workload Identity を使用する(推奨)

利用可能なクラスタモード
Standard クラスタ・Autopilot クラスタ

Workload Identity では、Kubernetes の ServiceAccount リソースGoogle Cloud の IAM サービスアカウント を紐付けることができます。
ここでは 2 種類のサービスアカウントが登場するので、わかりやすいように「Kubernetes の ServiceAccount リソース」を「KSA」、「Google Cloud の IAM サービスアカウント」を「GSA」と記載します。

KSA は本来、Kubernetes API に対するアクセス権を Pod に付与するための Kubernetes リソースです。
Workload Identity を使用すると、サービスアカウントキーを使用することなく、Pod に対して KSA を設定するだけで、KSA に紐付いた GSA の権限で Google Cloud APIs にアクセスできるようになります。

Workload Identity を使用する場合

GKE で Workload Identity を使用する方法

ここからは、スタンダードモードの GKE クラスタで Workload Identity を有効化し、Pod から Google Cloud APIs にリクエストを送信してみます。

参考:Workload Identity を使用する

GKE クラスタでWorkload Identity を有効化する。

Autopilot モードの GKE クラスタでは、デフォルトで Workload Identity が有効化されているため、この手順を実施する必要はありません。

まず、GKE クラスタで Workload Identity を有効化します。
ここでは既存のクラスタに対して更新を行っていますが、クラスタの作成時にあらかじめ有効化することもできます。

# クラスタで Workload Identity を有効化する
$ gcloud container clusters update {クラスタ名} \
--zone={クラスタのゾーン} \
--workload-pool={プロジェクト名}.svc.id.goog

※ ゾーンクラスタの場合は --zone オプション、それ以外の場合は --region オプションを指定します。

ノードプールで Workload Identity を有効化する

ノードプールに対しても Workload Identity を有効化していきます。
この手順についても Autopilot モードの GKE クラスタでは実施する必要はありません。

Workload Identity を有効化すると、Pod からノードに紐付いたサービスアカウントが使用できなくなるため、Google Cloud APIs を使用している既存のワークロードが中断する可能性があります。
本番環境では、 Workload Identity を有効化したノードプールを新たに作成し、既存のノードからワークロードを移行することをおすすめします。

# ノードプールで Workload Identity を有効化する
$ gcloud container node-pools update {ノードプール名} \
--cluster={クラスタ名} \
--zone={ノードプールのゾーン} \
--workload-metadata=GKE_METADATA

Google Cloud の サービスアカウント(GSA)を作成する

Google Cloud APIs にアクセスするための GSA を作成します。

# GSA を作成する
$ gcloud iam service-accounts create {GSAの名前} --project={プロジェクト名}

作成した GSA に対して任意の IAM ロールを紐付けます。
当記事では Kubernetes Engine クラスタ閲覧者 (roles/container.clusterViewer) を GSA に紐付けます。

# GSA に IAM ロールを紐付ける
$ gcloud projects add-iam-policy-binding {プロジェクト名} \
--member "serviceAccount:{GSAの名前}@{プロジェクト名}.iam.gserviceaccount.com" \
--role "roles/container.clusterViewer"

Kubernetes の ServiceAccount リソース(KSA)を作成する

以下のマニフェストファイルを用いて、GKE クラスタに KSA を登録します。
アノテーション iam.gke.io/gcp-service-account の値に、この KSA に紐付ける GSA を指定します。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: {KSAの名前}
  annotations:
    # Workload Identity で紐付ける GSA を指定する
    iam.gke.io/gcp-service-account: {GSAの名前}@{プロジェクト名}.iam.gserviceaccount.com

KSA と GSA を紐付ける

GSA に対する Workload Identity User (roles/iam.workloadIdentityUser) ロールを KSA に紐付けることで、KSA が GSA の権限を借用できるようになります。

# KSA と GSA を紐付ける
$ gcloud iam service-accounts add-iam-policy-binding {GSAの名前}@{プロジェクト名}.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:{プロジェクト名}.svc.id.goog[{KSAを作成したNamespace}/{KSAの名前}]"

例えば、各リソースを以下のように作成したとします。

項目 設定値
プロジェクト名 myproject
GSAの名前 gsa-workloadid
KSAの名前 ksa-workloadid
KSAを作成したNamespace default

この場合、KSA と GSA を紐付けるコマンドは以下のようになります。

# KSA と GSA を紐付ける(例)
$ gcloud iam service-accounts add-iam-policy-binding gsa-workloadid@myproject.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:myproject.svc.id.goog[default/ksa-workloadid]"

API アクセスの確認

KSA と GSA の紐付けが完了したので、GKE クラスタに Pod をデプロイし、Google Cloud APIs にリクエストを送信してみます。
以下のマニフェストファイルを用いて、GSA に紐付けた KSA を使用する Pod を作成します。

apiVersion: v1
kind: Pod
metadata:
  name: gcloud-pod-workloadid
spec:
  containers:
  - name: cloud-sdk
    image: google/cloud-sdk:slim
    command: ["sleep", "infinity"]
  serviceAccountName: {KSAの名前}
  nodeSelector:  # Autopilot モードの場合は不要
    iam.gke.io/gke-metadata-server-enabled: "true"

クラスタ内に Workload Identity が有効化されているノードとされていないノードが混在している場合、nodeSelector を記述して Workload Identity が有効なノードで Pod を起動するように明示します。
Autopilot モードのクラスタでは各ノードで Workload Identity が必ず有効化されるため、この nodeSelector は不要です。

Pod のデプロイが完了したら、GKE クラスタの一覧を出力する gcloud コマンドを Pod 上で実行してみます。
KSA に紐付いた GSA の権限を借用することで、GKE クラスタの一覧を取得することができます。

# Pod 上で gcloud コマンドを実行
$ kubectl exec -it gcloud-pod-workloadid -- gcloud container clusters list

----- 出力例 -----
NAME                LOCATION           MASTER_VERSION   MASTER_IP      MACHINE_TYPE  NODE_VERSION     NUM_NODES  STATUS
mycluster-standard  asia-northeast1-b  1.24.9-gke.3200  35.243.123.64  e2-small      1.24.9-gke.3200  1          RUNNING

佐々木 駿太 (記事一覧)

G-gen最北端、北海道在住のクラウドソリューション部エンジニア

2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2024に選出。好きなGoogle CloudプロダクトはCloud Run。

趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。