Cloud Run で受け口のサービスと裏の処理用サービスを分けたとき、裏のサービスはインターネットから呼べないようにして、受け口のサービスからだけ呼べるようにしたいことがあります。この記事では、呼び出される側のサービスに roles/run.invoker を特定のサービスアカウントだけに付け、呼び出す側から ID トークンを付けて呼ぶまでの手順をまとめます。前提は Google Cloud の Cloud Run(サービス)と gcloud CLI で、2026年11月時点の公式ドキュメントに沿っています。
以下では、呼び出す側を frontend、呼び出される側を worker と呼びます。
全体の流れ
- 呼び出す側(
frontend)専用のサービスアカウントを作る frontendをそのサービスアカウントで動かすworkerを認証必須にする(allUsersを外す)workerのroles/run.invokerを、frontendのサービスアカウントにだけ付けるfrontendのコードで ID トークンを取り、Authorizationヘッダーに付けて呼ぶ
Cloud Run のサービスは、何も指定しなければ認証必須(非公開)で作られます。公開にしたのは --allow-unauthenticated を付けたか、allUsers に roles/run.invoker を付けたときです。
呼び出す側のサービスアカウントを用意する
1. サービスアカウントを作る
サービスアカウントを指定しないと、Cloud Run は Compute Engine のデフォルトのサービスアカウント(PROJECT_NUMBER-compute@developer.gserviceaccount.com)で動きます。これに roles/run.invoker を付けると、同じプロジェクトでこのアカウントを使う他のリソースまで worker を呼べてしまいます。公式ドキュメントも、サービスごとに権限を絞ったユーザー管理のサービスアカウントを使うことを勧めています。
gcloud iam service-accounts create frontend-sa \
--display-name="frontend service"
作られるメールアドレスは frontend-sa@PROJECT_ID.iam.gserviceaccount.com です。
2. 呼び出す側をそのサービスアカウントで動かす
gcloud run deploy の --service-account に指定します。このサービスアカウントが、動いているリビジョンの身元になります。
gcloud run deploy frontend \
--image=IMAGE_URL \
--region=asia-northeast1 \
--service-account=frontend-sa@PROJECT_ID.iam.gserviceaccount.com
呼び出される側を特定のサービスアカウントだけに開ける
3. 認証必須にする
新しく出すときは --no-allow-unauthenticated を付けます。
gcloud run deploy worker \
--image=IMAGE_URL \
--region=asia-northeast1 \
--no-allow-unauthenticated
すでに公開しているサービスは、まず今のポリシーを確かめます。
gcloud run services get-iam-policy worker --region=asia-northeast1
members に allUsers があれば外します。
gcloud run services remove-iam-policy-binding worker \
--region=asia-northeast1 \
--member=allUsers \
--role=roles/run.invoker
あわせて、呼び出し元の IAM チェックそのものを切っていないか(--no-invoker-iam-check で出していないか)も確かめます。切っている場合は、次のコマンドで有効に戻します。
gcloud run services update worker \
--region=asia-northeast1 \
--invoker-iam-check
4. 呼び出す側のサービスアカウントに roles/run.invoker を付ける
プロジェクト全体ではなく、worker というサービス1つにだけ付けます。--member は serviceAccount:メールアドレス の形で書きます。
gcloud run services add-iam-policy-binding worker \
--region=asia-northeast1 \
--member='serviceAccount:frontend-sa@PROJECT_ID.iam.gserviceaccount.com' \
--role='roles/run.invoker'
呼べればよいだけなら、roles/run.invoker の代わりにサービスを呼ぶ権限だけを持つ roles/run.servicesInvoker も使えます。roles/run.invoker はジョブの実行やジョブ実行の取り消しもできるロールです。
呼び出す側から ID トークンを付けて呼ぶ
5. ID トークンを取ってヘッダーに付ける
Cloud Run の上で動いているコードは、メタデータサーバーから自分のサービスアカウントの ID トークンを取れます。audience には呼び出す先のサービスの URL(https://worker-xxxxx.a.run.app の形)を渡します。
curl "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://WORKER_URL" \
-H "Metadata-Flavor: Google"
取ったトークンを、worker へのリクエストの Authorization ヘッダーに付けます。
Authorization: Bearer ID_TOKEN
Python なら google-auth のライブラリで同じことができます。
import urllib.request
import google.auth.transport.requests
import google.oauth2.id_token
audience = "https://WORKER_URL"
auth_req = google.auth.transport.requests.Request()
id_token = google.oauth2.id_token.fetch_id_token(auth_req, audience)
req = urllib.request.Request(f"{audience}/tasks")
req.add_header("Authorization", f"Bearer {id_token}")
with urllib.request.urlopen(req) as resp:
print(resp.status)
ID トークンは作られてからおよそ1時間で切れます。長く動くプロセスでは、呼ぶたびに取り直すか、期限の前に取り直します。
動作を確認する
worker のポリシーに、呼び出す側のサービスアカウントだけが載っていることを確かめます。
gcloud run services get-iam-policy worker --region=asia-northeast1
期待する出力(抜粋):
bindings:
- members:
- serviceAccount:frontend-sa@PROJECT_ID.iam.gserviceaccount.com
role: roles/run.invoker
allUsers が載っていなければ、インターネットからトークン無しで worker を呼んでも通りません。手元から curl https://WORKER_URL を打って、リクエストが拒否されることを確かめます。そのうえで frontend 経由の処理が通れば設定は完了です。
うまくいかないとき
- メタデータサーバーがトークンを返さない:
Metadata-Flavor: Googleヘッダーが無いと、メタデータサーバーはリクエストを拒否します - 手元の PC でメタデータサーバーの URL が引けない: メタデータサーバーから取る方法は Google Cloud の外では使えません。手元で試すときは別の方法でトークンを取ります
- トークンを付けても通らない:
audienceが呼び出す先のサービス URL になっているかを確かめます。トラフィックタグ付きの URL へ送るときも、カスタムオーディエンスを設定していなければaudはサービスの URL のままにします。カスタムドメインはaudに使えません AuthorizationとX-Serverless-Authorizationの両方を付けている: 両方あると Cloud Run はX-Serverless-Authorizationしか見ません。アプリ側でAuthorizationを別の用途に使うときは、Cloud Run 向けのトークンをX-Serverless-Authorization: Bearer ID_TOKENに入れます- しばらく前に動いていた処理が急に通らなくなった: トークンの期限(約1時間)が切れていないかを確かめます
--no-allow-unauthenticatedを付けてすぐはまだ公開のまま: この設定は反映まで少し時間がかかることがありますallUsersをプロジェクトの IAM に付けようとして失敗する:allUsersとallAuthenticatedUsersはプロジェクト単位には付けられず、サービスごとに付けるものです



コメント