UPSIDER Tech Blog

Workload Identity FederationをGKEに導入しセキュリティを強化する

こんにちは、UPSIDER Anti-Fraud チームの小針です。

UPSIDERでは、Google CloudにおけるKubernetesクラスタのマネージドサービスであるGKEを活用し、サービスの成長に伴い多くのワークロード(Kubernetes上で稼働するアプリケーション)を稼働させています。

これらのGKE上のワークロードが、SpannerやFirestoreなどのGoogle Cloudのサービスにアクセスする場合、安全かつ管理しやすい認証認可の仕組みが不可欠です。

そのため、GKEワークロードからGoogle Cloudサービスへ安全にアクセスするために推奨される方法である「Workload Identity Federation for GKE」の導入を行いました。Anti-Fraudチームでは、1月末にWorkload Identity Federation for GKEを有効化後、2月内に管轄のワークロード全てに導入を済ませています。

本記事では「Workload Identity Federation for GKE」の概要、従来の認証方法の課題について解説し、そして具体的な導入方法をサンプルコード(TerraformやKubernetesマニフェストファイル)と共に説明します。

Workload Identity Federation for GKE って何?

Workload Identityは、サービスアカウントのキーを用いるといったようなセキュリティリスクの高い方法を必要とせずに、GKE上のPodに対し、Google CloudのIAM 権限を付与するための認証方式です。

Workload Identityを利用することで、GKE上で稼働する各アプリケーションに対して、個別にIDと認可を割り当てることが可能となり、最小権限の原則を遵守した運用が実現します。

Workload Identityの用語整理

Workload Identityを扱う上で、以下の2種類のサービスアカウントが登場しますので、混乱しないようここで説明しておきます。

  • Kubernetes Service Account (以下、KSA):GKE Pod内で実行されるプロセスにIDを提供するKubernetesリソース

  • IAM Service Account (以下、GSA):アプリケーションがGoogle Cloud APIへの認可された呼び出しを行うためのGoogle Cloudリソース

従来の認証方法とその課題

Workload Identityが推奨される以前は、主に以下の2つの方法が使用されていました。これらの方法はセキュリティ上の課題を抱えており、Workload Identityの導入が推奨される理由となっています。

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

GKEのノードはCompute Engineのインスタンスで構成されており、ノードプールに設定されたGSAがノードのインスタンスに紐付けられます。このGSAは、ノード上で起動する全てのPodで自動的に使用されます。

課題点
ノードプール作成時にGSAを指定しない場合、Compute EngineのデフォルトGSA(roles/editorが付与されていることが多い)が紐付きます。 そのノードにデプロイされた全てのPodが同じGSAを使用するため、特定のPodのために権限を付与すると、他のPodもその権限を利用できてしまい、過剰な権限付与につながります。

2.サービスアカウントキーをSecretとして利用する

GSAのキーをエクスポートし、Kubernetes Secretとして登録し、Podにボリュームとしてマウントする方法です。これにより、Podごとに異なるサービスアカウントキーを使用できるため、最小権限の原則を遵守した運用は可能になります。

課題点
サービスアカウントキーは適切に管理されない場合、セキュリティリスクとなります。 つい先日、npmのJavaScriptパッケージをターゲットとしたサプライチェーン攻撃によりクラウドサービスの認証情報が窃取されるといったことが起こりました(参考)。 上記例のように、認証情報は悪意ある人に盗まれないよう慎重に取り扱わなければなりません。しかし、キーの漏洩対策やライフサイクル管理は煩雑です。

Workload Identity Federationの仕組み

Workload Identityをクラスタで有効化すると、GKEは以下の動作を行います。

1.ワークロードIDプールの作成: クラスタのGoogle Cloudプロジェクトに対して、固定のワークロードIDプール(PROJECT_ID.svc.id.goog形式)が作成されます。これは、IAMがKubernetes認証情報を信頼するための名前付け形式を提供します。

2.IDプロバイダ登録: GKEクラスタがワークロードIDプール内のIDプロバイダとして登録されます。

3.GKEメタデータサーバーのデプロイ: すべてのノードにGKEメタデータサーバーがデプロイされ、ワークロードからの認証情報リクエストをインターセプトします。

ワークロードがGoogle Cloud APIにアクセスするためのリクエストを送信すると、認証が行われます。認証のフローは公式ドキュメントにシーケンス図付きで説明されているのでそちらをご覧ください。

Workload Identityの導入方法

Workload Identityの導入には、主に下記2つのアプローチがあります。

1.KSAとGSAを紐づける方法
2.KSAにIAMロールを直接付与する方法(2024年3月のアップデートで追加)

2の方法ではGSAの作成が不要なため、GSAの管理コストが抑えられます。基本的には2の方法で良いと思いますが、1の方法でないと対応できないケースもあります。それは、VPC Service Controlsでセキュリティ境界を適用している環境で、境界をまたいでリソースにアクセスするケースです。VPC Service ControlsではWorkload Identity Federation for GKEがサポートされておらず、このケースではGSAの権限借用が必要となります。

私が導入を行った際は2の方法で行いました。本記事では2の方法を紹介します。(サンプルコードは、Workload Identityの設定に必要な部分だけ抜き出しています。)

設定手順

  1. GKEクラスタでWorkload Identityを有効化する(Terraform)

    Autopilotクラスタではデフォルトで有効化されています。
    Standardクラスタの場合は下記のように設定します。

    • GKEクラスタの設定

      resource "google_container_cluster" "hoge_cluster" {
      ...
        workload_identity_config {
          workload_pool = 
        "${data.google_project.project.project_id}.svc.id.goog"
        }
      }
      
    • ノードの設定(クラスタでWorkload identityを有効化していないと効きません)

      resource "google_container_node_pool" "hoge_nodes" {
      ...
            node_config {
                ...
                workload_metadata_config {
                        mode = "GKE_METADATA"
                  }
            }
      }
      
  2. KSAリソースを作成する(Kubernetesマニフェストファイル)
    Podで使用するKSAを作成します。

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: hoge-serviceaccount
      namespace: hoge-ns
      ...
    
  3. KSAにIAMロールを紐付ける(Terraform)
    KSAをIAMのプリンシパルとして識別するためのWorkload Identity Federation特有の文字列(プリンシパル識別子)を使用して、IAMロールを直接付与します。
    プリンシパル識別子の形式:principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/NAMESPACE/sa/SERVICE_ACCOUNT
    例としてSpanner読取権限を付与する場合:

    resource "google_project_iam_member" "project" {
      project = "your-project-id"
      role    = "roles/spanner.databaseUser"
      member  = "principal://iam.googleapis.com/projects/1234/locations/global/ \
    workloadIdentityPools/your-project-id.svc.id.goog/subject/ns/hoge-ns/sa/hoge-serviceaccount"
    }
    

    注)google_project_iam_binding でも紐付けられますが、扱いに注意が必要です(参考)。

  4. Pod側で設定を行う(Kubernetesマニフェストファイル)
    作成したKSAをPodマニフェストのspec.serviceAccountNameに設定することで、Podは紐付けられた権限でGoogle Cloud APIにアクセスできます。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
     name: hoge
    spec:
     template:
      spec:
        serviceAccountName: hoge-serviceaccount
    

まとめ

鍵の流出リスクや過剰な権限付与のリスクを解消できるため、Workload Identityの利用はGKEにおけるセキュリティ施策として非常に有効です。 UPSIDERでも引き続き、安全で効率的なインフラストラクチャ運用を目指していきます。 読んでいただきありがとうございました!

補足

セキュリティリスクに対応し追従するためには、公式のリリースノートパッチを確認して計画的な変更をお勧めします。

参考

We Are Hiring !!

UPSIDERでは現在積極採用をしています。
ぜひお気軽にご応募ください。 herp.careers

herp.careers

UPSIDER Engineering Deckはこちら📣

speakerdeck.com