💫

見て見ぬふりをしない、権限とWorkload Identity(Google Cloud)

2024/04/28に公開

はじめに

Google Cloudを使う際、最も頻繁に遭遇するエラーは「権限が足りない」というものでした。特に新しいプロジェクトを立ち上げ、CI/CDの構築に取り組む際にこのエラーに何度も直面し、時間を浪費してしまいました。
この経験から、Google Cloudの権限管理を深く知ることが重要であると痛感しました。そこで、体系的にGoogle Cloudの権限管理を学び、その成果をこの記事でわかりやすく共有したいと思います。

この記事を読んでほしい人

  • Google Cloudにおける権限、ロール、プリンシパル、ポリシーの意味と関係性を説明できない人
  • Workload Identityをなんとなく使っている人

この記事で伝えたいこと

  • Google Cloudにおいて、権限を複数まとめ、使いやすい集合にしたものがロール
  • リソースにアクセスするユーザーのことをプリンシパルといい、ロールとプリンシパルの関係をポリシーで表現している
  • この一連の管理方法がIAM
  • Workload Identityはセキュリティリスクのあるサービスアカウントキーを発行しないで済む
  • Workload Identityを支える認証方法がOIDC

この記事で説明しないこと

  • OIDCの具体的な認証プロセス

IAMを理解する

IAM(Identity and Access Management)は、次の3つの要素を管理するシステムです。

  • アクセスするユーザー(ID)
  • アクセスされるリソース
  • そのとき可能な操作(権限)

これらをきめ細かく、かつ最小限の権限で管理するために、Google Cloudはさまざまな機能を導入しています。1つずつ順番に見ていきましょう。

プリンシパル

まず、Google CloudリソースにアクセスするIDのことをプリンシパルといいます。このIDは以下のいずれかです。

  • Googleアカウント
  • サービス アカウント
  • Googleグループ
  • Google Workspaceアカウント
  • Cloud Identityドメイン
  • 認証済みのすべてのユーザー
  • すべてのユーザー

Google Cloudではプリンシパルに権限を付与します。

権限

まず、一般的な用語として使われる「権限」とは意味が異なっています。Google Cloudにおける権限とは、特定のリソースに対して行うことのできる一つの操作のことです。
具体的には「Cloud Storageに対してWriteできる」というのが権限であり、「Cloud StorageについてWrite、Readなどすべての操作が行える」というものは権限とは言いません(後述するロールになります)。

すべての権限は互いに排他であり、1つの権限がアクセスできるリソースは1つのみに限られます。

この細かな権限は、最小権限の原則を構成しやすくしているものの、多数のプリンシパルに対して膨大な数の権限を付与する必要があり、メンテナンスが十分にできないという問題点があります。

そこでロールという概念を導入します。

ロール

ロールは、複数の権限をまとめたセットです。Google Cloudでは権限を直接プリンシパルに付与することはできず、このロールをプリンシパルに付与することで権限を与えます。

例えばCloud Run Developerロールは画像のような権限のセットです。これらの権限を人の手でうまく管理できるかと言われると、ほとんどの場合は無理でしょう。ロールを使用することによって最小権限と管理の容易さを実現しています。


https://cloud.google.com/iam/docs/understanding-roles#run.developer

ロールの種類

ロールには基本ロール、事前定義ロール、カスタムロールの三種類があります。

基本ロールはOwnerEditorViewerの3つがあります。逆に言うと3つしかありません。リソースごとに細かな権限の設定が全くできていない基本ロールは、現在非推奨となっています。

事前定義ロールは先ほど例に挙げたCloud Run Developerロールのように、リソースごとに適切な権限を割り当てたロールです。プリンシパルには基本的にこの事前定義ロールを割り当てます。

カスタムロールは、自分で権限を割り当てたロールです。事前定義ロールでは最小権限を実現できない場合に使用します。

ロールの決定方法

まず必要となる権限を探します。実際に操作を行ってみて、エラーメッセージから不足している権限を特定しても良いと思います。
必要な権限を以下のページで検索し、該当のロールから適切なものを見つけることができます。

https://cloud.google.com/iam/docs/permissions-reference

ポリシー

プリンシパルとロールを結びつけるのがポリシーです。
ポリシーは以下のような構造になっています。

{
  "bindings": [
    {
      "role": "roles/storage.objectAdmin",
      "members": [
        "user:ali@example.com",
        "serviceAccount:my-other-app@appspot.gserviceaccount.com",
        "group:admins@example.com",
        "domain:google.com"
      ]
    },
    {
      "role": "roles/storage.objectViewer",
      "members": [
        "user:maria@example.com"
      ]
    }
  ]
}

複数存在するロールに対して複数のプリンシパルを割り当てる、いわば中間テーブルのような構造をしています。
ポリシーをプリンシパルとロールから分離することで、権限をより管理しやすくなっていると思います。

チェック

以下の質問に対して自分なりに答えることで理解を深めましょう!

  • IAMとは何ですか?
  • ロールという概念が導入されたのはなぜですか?
  • プリンシパルに付与するロールはどのように決定しますか?

Workload Identityを理解する

Workload Identityは、外部からGoogle Cloudリソースにアクセスする際のセキュリティリスクを低減する目的で提供されています。しかし、その必要性を理解するためには、Workload Identityがない場合と比較する必要があります。

Workload Identityの必要性

GitHub ActionsからGoogle Cloudを操作する状況を考えます。

外部からリソースを操作する場合、プリンシパルとしてサービスアカウントを使用します。このサービスアカウントには適切な権限が付与されているものとします。
GitHub Actionsがサービスアカウントを利用してリソースにアクセスするには、サービスアカウントキーを発行する必要があります。
GitHub Actionsはサービスアカウント、サービスアカウントキーを使って無事にリソースにアクセスできました。
しかし、この発行したサービスアカウントキーには、常に漏洩リスクが伴います。
サービスアカウントとサービスアカウントキーが漏洩してしまった場合、外部から悪意のある攻撃を受ける覚悟をしなければなりません。漏洩が発覚するのは往々にして攻撃があってからではないでしょうか。

このようなリスクを回避するために使われるのがWorkload Identityです。

Workload Identityの概要

Workload Identityは、OIDC (OpenID Connect) を用いて期限付きのトークンを発行することで、セキュアに認証と認可の両方を行い、外部IDに対して任意のサービスアカウントになりすますことができるロールを付与する仕組みです。

Workload Identityプール

Workload Identityは外部IDを管理しているエンティティです。
dev、stg、prodなど環境が分かれている場合は、外部IDに対してより細かな権限制御を行うために複数のプールを作成することが推奨されます。

https://cloud.google.com/iam/docs/workload-identity-federation?hl=ja#pools

Workload Identityプール プロバイダ

IAMにおけるポリシーのように、外部IDとGoogle Cloudの関係を持っています。プロバイダの責務は、GitHub Actionsからのリクエストに対して情報を含んだトークンを発行することです。このトークンには様々な属性を指定でき、その属性をもとに「特定のリポジトリ以外からのアクセスは拒否する」といった設定も可能です。

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

先述したトークンをGoogle Cloudが検証し、正しいことが確認されれば、外部IDに対してGitHub Actionsが指定したサービスアカウントになりすますロールが付与されます。

この権限借用によって、サービスアカウントキーを発行することなくサービスアカウントのロールを活用してリソースにアクセスできます。

GitHub Actionsの例

以下のGitHub Actionsのサンプルをもとに解説します。
https://github.com/google-github-actions/example-workflows/blob/main/workflows/deploy-cloudrun/cloudrun-docker.yml

トークンの取得

Authアクションを使用することで、OIDCトークンを取得できます。このときid-tokenのpermissionを許可することで、今後のアクションにおいてトークンを参照できます。

https://github.com/google-github-actions/example-workflows/blob/f601e097144431f49ab9abb1f8a46e7f4e6a5e5c/workflows/deploy-cloudrun/cloudrun-docker.yml#L65-L67

Authアクションでは、Workload Identityプロバイダと権限を借用したいサービスアカウントの情報が含まれています。

https://github.com/google-github-actions/example-workflows/blob/f601e097144431f49ab9abb1f8a46e7f4e6a5e5c/workflows/deploy-cloudrun/cloudrun-docker.yml#L74-L80

Docker認証とpush

アクセストークンを使って、Artifact Registryにイメージをプッシュしています。トークンによってサービスアカウントが指定されているため、サービスアカウントはArtifact Registryに対する書き込み権限を持っている必要があります。

https://github.com/google-github-actions/example-workflows/blob/f601e097144431f49ab9abb1f8a46e7f4e6a5e5c/workflows/deploy-cloudrun/cloudrun-docker.yml#L93-L99

https://github.com/google-github-actions/example-workflows/blob/f601e097144431f49ab9abb1f8a46e7f4e6a5e5c/workflows/deploy-cloudrun/cloudrun-docker.yml#L110-L113

Cloud Runにデプロイ

GoogleのActionsではcore.getIDToken()というメソッドによって、Authアクションによって書き込まれたトークンを取得しています。
サービスアカウントはCloud Runに対する十分な権限と、Artifact Registryに対する読み取り権限を持っている必要があります。

https://github.com/google-github-actions/example-workflows/blob/f601e097144431f49ab9abb1f8a46e7f4e6a5e5c/workflows/deploy-cloudrun/cloudrun-docker.yml#L117-L124

最後に

この記事を書いたことで、Google Cloud周りの権限に対する理解が十分に深まったと思います。いつかこの記事を自分で見返しても参考になるように書いたつもりです。お読みいただき、ありがとうございました。

参考

https://cloud.google.com/iam/docs/overview?hl=ja
https://cloud.google.com/iam/docs/workload-identity-federation?hl=ja
https://cloud.google.com/blog/ja/products/devops-sre/deploy-to-cloud-run-with-github-actions
https://christina04.hatenablog.com/entry/workload-identity-federation
https://zenn.dev/cloud_ace/articles/7fe428ac4f25c8
https://zenn.dev/cybozu_ept/articles/c241f28b4f32ec

Discussion