GCPを触り始めたときに、AWS経験者が特に混乱しやすい概念の1つが Service Account(サービスアカウント) です。
名前だけを見ると「ユーザーアカウントの一種?」と思いやすいですが、実際には違います。
GCPのService Accountは、ざっくり言うと、
VMやCloud Run、バッチ処理、アプリケーションなどがGCPリソースへアクセスするときに使う、システム用のIdentity
です。
AWSに慣れている人向けに言うと、
Service Account ≒ ワークロード用IAM Role
と考えるとかなり理解しやすくなります。
この記事では、Service Accountの基本からIAMとの関係、JSONキー、Compute Engine、Cloud Run、ローカル開発での使い方までまとめます。
Service Accountとは何か
GCPでは「誰が操作しているか」をIdentityとして扱います。
大きく分けると、人間とシステムがあります。
人間
↓
Googleアカウント
↓
GCPへアクセス
プログラム
↓
Service Account
↓
GCPへアクセス
例えば、人間であれば次のようなGoogleアカウントを使います。
tanaka@example.com
一方、プログラム用のService Accountは次のような形式です。
web-api-prod@my-project.iam.gserviceaccount.com
つまり、
Google Account
= 人間用Identity
Service Account
= システム・アプリ用Identity
という違いがあります。
AWSでいうと何に相当するのか
AWS経験者向けに整理すると、以下の対応関係が近いです。
| AWS | GCP |
|---|---|
| IAM User / IAM Identity Centerユーザー | Googleアカウント |
| IAM Role | Service Account |
| IAM Policy | IAM Role |
| EC2 Instance Profile | VMにService Accountをアタッチ |
| ECS Task Role | Cloud Run / GKEなどのService Account |
| Lambda Execution Role | Cloud FunctionsのService Account |
ここで注意したいのが、GCPの「IAM Role」です。
AWSでは、
IAM Role
がIdentityと権限の両方を持っているように見えます。
一方GCPでは、
Service Account
= 誰として動くか
IAM Role
= 何をしてよいか
と分かれています。
例えば、
Service Account
web-api-prod@my-project.iam.gserviceaccount.com
↓
IAM Role
roles/storage.objectViewer
とすると、
web-api-prodというシステムIdentityはCloud Storageのオブジェクトを閲覧できる
という意味になります。
Service AccountとIAM Roleの関係
GCP IAMを理解するときは、次の3要素で考えると分かりやすいです。
Principal
↓
Role
↓
Resource
Principal
操作する主体です。
例えば、
Googleアカウント
Service Account
Google Group
などがあります。
Role
「何をしてよいか」という権限セットです。
例えば、
roles/storage.objectViewer
roles/storage.objectCreator
roles/storage.objectAdmin
などがあります。
Resource
対象となるGCPリソースです。
例えば、
Project
Cloud Storage Bucket
Compute Engine
Secret Manager
などです。
つまり、
web-api-prod
↓
Storage Object Viewer
↓
gs://my-images-prod
のような関係になります。
Service Accountは何に使うのか
代表的な用途は以下です。
Compute Engine
Cloud Run
Cloud Functions
GKE
バッチ処理
CI/CD
外部アプリケーション
例えばCompute Engine上のWebアプリがCloud Storageを読む場合、
Compute Engine
↓
Service Account
↓
Cloud Storage
という構成になります。
AWSでいう、
EC2
↓
IAM Role
↓
S3
とほぼ同じです。
VMにはService Accountを何個付けられるのか
Compute EngineのVMに直接アタッチできるService Accountは、基本的に1つです。
VM
↓
Service Account
ただし、そのService Accountには複数のIAM Roleを付与できます。
VM
↓
web-api-prod
├─ Storage Object Viewer
├─ Cloud SQL Client
└─ Secret Manager Secret Accessor
そのため、
Service Accountを複数付ける
のではなく、
1つのService Accountに必要なRoleを複数付ける
という設計が一般的です。
処理ごとに権限を分けたい場合
1台のVM内でも、処理ごとに権限を分けたい場合があります。
例えば、
Webアプリ
→ Cloud Storage読み取りのみ
デプロイ処理
→ Artifact Registry書き込み
のようなケースです。
この場合はService Account Impersonationを利用できます。
VM
↓
vm-base Service Account
↓
Impersonation
├─ storage-reader
└─ deployer
AWSでいう、
IAM Role
↓
sts:AssumeRole
↓
別IAM Role
に近い考え方です。
Service Account IDの形式
Service Accountは次のようなメールアドレス形式になります。
SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com
例えば、
web-api-prod@my-project.iam.gserviceaccount.com
です。
このうち、
web-api-prod
がService Account IDです。
命名規則としては、例えば、
<system>-<role>-<env>
のようにすると管理しやすいです。
例:
web-api-prod
web-api-stg
batch-worker-prod
image-uploader-dev
Service AccountのJSONキーとは
Service Accountでは、JSON形式の秘密鍵を発行することもできます。
例えば、
{
"type": "service_account",
"project_id": "my-project",
"private_key_id": "xxxxxxxx",
"private_key": "-----BEGIN PRIVATE KEY-----\n...",
"client_email": "web-api-dev@my-project.iam.gserviceaccount.com",
"client_id": "1234567890"
}
このJSONファイルを使うことで、アプリケーションはそのService Accountとして認証できます。
AWSでいうと、
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
をファイル化したものに近いです。
特に重要なのが、
private_key
です。
この秘密鍵が漏洩すると、そのService Accountとしてアクセスされる可能性があります。
JSONキーはできるだけ使わない
GCP上で動作するアプリケーションでは、Service AccountのJSONキーは基本的に不要です。
例えばCompute Engineでは、
Compute Engine
↓
Service Account
↓
Metadata Server
↓
短期アクセストークン
↓
GCP API
という流れで認証されます。
AWSでいう、
EC2
↓
IAM Role
↓
Instance Metadata Service
↓
一時Credential
とほぼ同じです。
そのため、以下のような運用は避けた方がよいです。
service-account.jsonをVMに配置
service-account.jsonをGit管理
Docker ImageにJSONをCOPY
Compute Engineでのベストプラクティス
Compute EngineでCloud Storageへアクセスする場合は、
Compute Engine
↓
専用Service Account
↓
必要最小限のIAM Role
↓
Cloud Storage
とします。
例えば画像Bucketを読むだけなら、
Service Account
web-api-prod
↓
roles/storage.objectViewer
↓
gs://images-prod
のようにします。
Project全体に広い権限を付けるよりも、可能であればBucket単位で権限を付与します。
Cloud Storageでよく使うRole
Cloud Storageでよく利用するRoleは次の通りです。
| 用途 | Role |
|---|---|
| オブジェクトを読む | roles/storage.objectViewer |
| オブジェクトを追加する | roles/storage.objectCreator |
| 読み書き・削除 | roles/storage.objectAdmin |
| Bucket設定まで管理 | roles/storage.admin |
例えば、
画像アップロードだけ
なら、
roles/storage.objectCreator
で十分な場合があります。
必要以上に、
roles/storage.admin
を付けないことが重要です。
ローカル開発ではどうするか
ローカル開発では、いくつか方法があります。
最も扱いやすいのはApplication Default Credentials、通称ADCです。
gcloud auth application-default login
を実行すると、Googleアカウントで認証できます。
その後、Pythonなどでは、
from google.cloud import storage
client = storage.Client()
だけで認証できます。
認証情報をコード側で明示的に指定する必要はありません。
本番と同じService Account権限で開発したい場合
ローカル環境でも、本番のService Accountと同じ権限でテストしたいことがあります。
その場合はService Account Impersonationが便利です。
gcloud auth application-default login \
--impersonate-service-account=web-api-dev@my-project.iam.gserviceaccount.com
イメージとしては、
開発者
↓
Google Account
↓
Service AccountをImpersonate
↓
Cloud Storage
です。
AWSでいう、
SSO User
↓
sts:AssumeRole
↓
Application Role
に近いです。
JSONキーをローカルで使う場合
どうしてもJSONキーを使う場合は、環境変数で指定します。
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.config/gcp/web-api-dev.json"
するとGoogle Cloud SDKが自動的に読み込みます。
Python側は、
from google.cloud import storage
client = storage.Client()
だけで動きます。
つまり、
アプリ
↓
Application Default Credentials
↓
GOOGLE_APPLICATION_CREDENTIALS
↓
Service Account JSON
という流れです。
JSONキーを置いてはいけない場所
特に避けたいのが、プロジェクトディレクトリへの配置です。
悪い例:
my-project/
├── app/
├── public/
├── .env
└── service-account.json
より安全なのは、
~/.config/gcp/
└── web-api-dev.json
のように、ソースコードと完全に分離することです。
また、
GitHub
Docker Image
publicディレクトリ
共有フォルダ
などにも配置しないようにします。
開発環境と本番環境は分ける
Service Accountは、環境ごとに分ける設計が安全です。
例えば、
web-api-dev
↓
dev-bucket
web-api-prod
↓
prod-bucket
のようにします。
ローカル開発用Service Accountが侵害されても、本番環境へアクセスできないようにするためです。
1つのService Accountを使い回さない
よくない設計として、
common-service-account
├─ Storage Admin
├─ Compute Admin
├─ Cloud SQL Admin
├─ Secret Manager Admin
└─ その他多数
のようなものがあります。
これは権限が大きくなりすぎるため危険です。
基本的には、
1 Service Account
=
1アプリケーション
または
1責務
くらいの粒度で分ける方が安全です。
例えば、
web-api-prod
image-uploader-prod
batch-worker-prod
backup-runner-prod
のように用途を分けます。
おすすめ構成
WebアプリケーションからCloud Storageへアクセスするケースでは、以下のような構成が分かりやすいです。
GCP Project
│
├─ Service Account
│ └─ web-api-prod
│
├─ Cloud Storage
│ └─ images-prod
│ └─ Storage Object Viewer
│ ↑
│ └─ web-api-prod
│
└─ Compute Engine / Cloud Run
└─ web-api-prodをアタッチ
アプリケーション側ではADCを使います。
from google.cloud import storage
client = storage.Client()
これだけです。
開発環境では、
ADC
または
Service Account Impersonation
を利用します。
本番では、
Compute Engine / Cloud Run
↓
Service Account直接アタッチ
とするのが綺麗です。
AWS経験者向けまとめ
AWSとGCPを対応させると、次のように理解できます。
AWS
EC2
↓
IAM Role
↓
IAM Policy
↓
S3
GCP
Compute Engine
↓
Service Account
↓
IAM Role
↓
Cloud Storage
最重要ポイントは、
AWS IAM Role
≈
GCP Service Account + IAM Role
と考えることです。
より正確には、
GCP Service Account
= Identity
GCP IAM Role
= Permissions
です。
そして運用上は、
GCP上のアプリ
→ Service Accountを直接アタッチ
ローカル開発
→ ADC / Impersonation
外部クラウド
→ Workload Identity Federation
JSONキー
→ 可能な限り避ける
という方針にすると、安全かつ管理しやすい構成になります。
まとめ
Service Accountは、GCPにおけるシステム用Identityです。
単なる「APIキー」ではなく、
このプログラムは誰として動くのか
を表現するための重要な仕組みです。
特に覚えておきたいのは次の点です。
- Service Accountはプログラム用Identity
- GCPのIAM Roleは権限セット
- VMやCloud RunにはService Accountを直接アタッチする
- 必要最小限のRoleだけを付与する
- Project全体ではなく、可能ならBucketなどリソース単位で権限を付ける
- JSONキーは長期Credentialなのでできるだけ避ける
- ローカルではADCやImpersonationを使う
- 開発環境と本番環境でService Accountを分ける
- 1つのService Accountに権限を集約しすぎない
AWSで、
EC2にAccess Keyを置かずIAM Roleを使う
のと同じ考え方で、
GCPでもJSON秘密鍵を置かずService Accountをワークロードに付与する
と理解すると、Service Accountの設計がかなり分かりやすくなります。