5〜7. Cloud Run 認証(cloudrun)
概要
Cloud Run 上のエージェントサービス(agent/・FastAPI)に実装された認証方式です。web/ の静的配信と API が同一オリジンで同居し、認証モードは環境変数 KASUGAI_AUTH_MODE で切り替えます(/auth-methods.json が動的に返されるため、モード変更にイメージ再ビルドは不要です)。
| control | モード | 保護範囲 | 用途 |
|---|---|---|---|
| 5 | cloudrun(認証のみ) | /api/* とデータのみ(静的は公開) |
公開アプリ+秘匿データ・設定。実装が最も軽い |
| 6 | cloudrun-gate(全ゲート) | 全リクエスト(Cookie ゲート) | デプロイ自体を到達不可にしたい場合 |
| 7 | cloudrun-iap | 全リクエスト(Cloud IAP) | Google アカウント運用できる組織内限定公開 |
Cloudflare の 3(Pages)→ 5、4(Workers)→ 6 がほぼ対応し、7 は Cloud Run 独自の追加方式です。「認証のみ・静的公開」は Cloudflare 側の制約(Pages が静的をゲートできない)の産物ではありますが、このプロジェクトでは web/ 自体が公開コードのため、静的秘匿の付加価値が小さく、まずは 5 の運用が現実的です。
構成
Cloud Run(agent/ FastAPI コンテナ)
├─ / … web/ 静的配信
├─ POST /api/gemini … 内蔵AI向け Gemini プロキシ(サーバーキー注入・認可必須)
├─ POST /api/auth … ID/PASS → {ok, token, kasc}(6 では Cookie も発行)
├─ POST /api/kasc … トークン必須 → GCS kasc/<projectId> へ書き込み
├─ GET|PUT /api/data/* … トークン必須 → GCS data/<key>(GET は署名付き URL へ 302)
└─ GET /api/capabilities … {tier:"cloudrun", features, auth:{mode}, ai, google?}
GOOGLE_MAPS_API_KEY 設定時は google.mapsApiKey を通知
Cloud Storage(KASUGAI_GCS_BUCKET)
├─ kasc/<プロジェクトID> … .kasc 本文(Cloudflare の kasc:<id> 相当)
└─ data/<キー> … gs:// で参照される秘匿データ
.kasc 内で gs://<キー> と書いたレイヤーは /api/data/<キー>?token=… に書き換えられて配信されます(Cloudflare の r2:// に相当。r2:// も移行用に読み替えられ、保存時は gs:// に正規化されます)。GET は既定で GCS V4 署名付き URL へ 302 リダイレクトするため、大きなデータもコンテナを通りません(KASUGAI_DATA_REDIRECT=0 でプロキシ配信に切替可・Range 対応)。
起動時に web/projects/*/*.kasc がストレージへ冪等シードされます(KASUGAI_SEED_KASC=0 で無効化)。秘匿したい .kasc は静的配信から削除してください — モード5 では web/projects/* は公開のままです。
利用シーン
- モード5:審査用デモ・一般公開しつつ設定とデータだけ保護したい。最も負荷が軽い推奨構成
- モード6:改造版
web/を秘匿したい、デプロイ自体の存在を限定したい場合 - モード7:Google アカウントでアクセス管理できる社内・組織内向け。認証コードはほぼ不要
モード別の設定(Canvas 側 ⇄ Cloud Run 側)
Canvas(web/)側:Cloud Run が web/ を同一オリジン配信する構成では、KASUGAI_AUTH_MODE から /auth-methods.json が動的に返るため web/ 側の設定変更は不要です(モード変更も再ビルド不要)。web/ を別途静的配信する場合だけ auth-methods.json の control を手動で 5/6/7 に設定してください。
| 設定項目 | 5 cloudrun | 6 gate | 7 iap |
|---|---|---|---|
KASUGAI_AUTH_MODE | 5 | 6 | 7 |
KASUGAI_AUTH_USER/PASS | 必須(Secret Manager) | 必須(同左) | 不要 |
KASUGAI_GCS_BUCKET | .kasc・データ利用時 | 同左 | .kasc 編集保存する場合のみ |
KASUGAI_IAP_AUDIENCE | − | − | 署名検証する場合 |
| デプロイフラグ | --allow-unauthenticated | --iap | |
| インフラ前提 | バケット+SA 権限(GCS 利用時) | OAuth 同意画面+iap.httpsResourceAccessor | |
デプロイ手順
リポジトリ直下の run-cloudrun.py が、API 有効化 → Artifact Registry → Cloud Build(ローカルの Docker 不要)→ Secret Manager → GCS バケット・権限付与 → gcloud run deploy → 動作確認までを一括で行います。Cloud Shell(ブラウザターミナル)でも実行できます。
python run-cloudrun.py --gemini-api-key KEY --maps-api-key MAPS_KEY \
--auth-mode 5 --auth-user ID --auth-pass PASS --bucket kasugai-data-xxxx
初回デプロイ後は非秘密の設定が kasugai_canvas.cloudrun.json(gitignore)に保存されるため、再デプロイは引数なしの python run-cloudrun.py(または python run.py --cloudrun)だけで実行できます。シークレットは Secret Manager の既存登録が再利用されます。agent/cloudrun.kasc が存在すると、デプロイ時に gs://<bucket>/kasc/default へアップロードされ、Cloud Run 版だけが web/projects/ とは別の初期 .kasc で動きます(ベースマップ透明+Google 3D ON+レイヤー OFF の提出用初期状態を同梱済み。起動時シードは既存オブジェクトを上書きしないため維持される。--kasc none で無効化・--kasc PROJ:FILE でプロジェクト指定可)。
手動で行う場合は agent/README.md の手順どおりです(コンテキストはリポジトリ直下)。以下は手動運用時の個別手順です。
-
GCS バケットを用意します(モード5/6 で .kasc・データを使う場合)。
gcloud storage buckets create gs://kasugai-data --location=asia-northeast1 -
実行サービスアカウントに権限を付与します(署名付き URL を使う場合)。
# signBlob 用(自身への Token Creator)+ バケットのオブジェクト管理 gcloud iam service-accounts add-iam-policy-binding SA_EMAIL \ --role=roles/iam.serviceAccountTokenCreator --member=serviceAccount:SA_EMAIL gcloud storage buckets add-iam-policy-binding gs://kasugai-data \ --role=roles/storage.objectAdmin --member=serviceAccount:SA_EMAIL gcloud services enable iamcredentials.googleapis.com -
署名 URL 方式を使う場合はバケットに CORS を設定します(302 後はクロスオリジンになるため必須)。
未設定でも動きますがgcloud storage buckets update gs://kasugai-data --cors-file=cors.json # cors.json: # [{"origin":["https://<app>.a.run.app"], "method":["GET","PUT"], # "responseHeader":["Content-Type","Content-Range","ETag"], "maxAgeSeconds":3600}]KASUGAI_DATA_REDIRECT=0(プロキシ配信)で運用してください。 -
ID/PASS を Secret Manager に登録します(モード5/6)。
gcloud secrets create kasugai-auth-user --data-file=- <<< admin gcloud secrets create kasugai-auth-pass --data-file=- <<< '********' -
デプロイします。
# モード5(認証のみ) gcloud run deploy kasugai-canvas \ --image asia-northeast1-docker.pkg.dev/PROJECT/kasugai/kasugai-canvas \ --region asia-northeast1 --allow-unauthenticated --min-instances 1 \ --set-env-vars KASUGAI_AUTH_MODE=5,KASUGAI_GCS_BUCKET=kasugai-data \ --set-secrets KASUGAI_AUTH_USER=kasugai-auth-user:latest,KASUGAI_AUTH_PASS=kasugai-auth-pass:latest # モード7(IAP。認証はインフラ側) gcloud run deploy kasugai-canvas --image ... --iap--min-instances 1はフロントの/health検出(1秒タイムアウト)がコールドスタートを逃さないための推奨設定です。 - 動作確認します。トップページでログイン画面(5/6)または Google ログイン(7)が出ること、誤パスワードで 401、ログイン後にレイヤーが表示されることを確認します。
環境変数
| 変数 | 用途 |
|---|---|
KASUGAI_AUTH_MODE | 5/6/7 で起動制御を有効化(未設定は認証なし) |
KASUGAI_AUTH_USER/KASUGAI_AUTH_PASS | モード5/6 の ID/PASS(Secret Manager 推奨) |
KASUGAI_TOKEN_SECRET | トークン署名鍵(未設定時は KASUGAI_AUTH_PASS) |
KASUGAI_GCS_BUCKET | .kasc・秘匿データの格納バケット(未設定時はローカル .datastore/・開発用) |
KASUGAI_DATA_REDIRECT | 0 で GET /api/data をプロキシ配信(既定 1=署名 URL へ 302) |
KASUGAI_SIGNED_URL_TTL | 署名 URL 有効期限秒(既定 900) |
KASUGAI_IAP_AUDIENCE | モード7 の IAP JWT audience(設定時は署名検証) |
KASUGAI_SEED_KASC | 0 で起動時の .kasc シードを無効化 |
GEMINI_API_KEY | Gemini API キー。/api/gemini でサーバーキー注入に使用(Secret Manager 推奨) |
GOOGLE_MAPS_API_KEY | Maps Platform キー。/api/capabilities 経由でブラウザへ配布(秘匿ではない。リファラ制限で保護) |
資産ごとの秘匿状況
| 資産 | 5 cloudrun | 6 cloudrun-gate | 7 cloudrun-iap |
|---|---|---|---|
| ID/PASS | ✅ 環境変数のみ Secret Manager 推奨 |
✅ 同左 | ✅ Google アカウント アプリは資格情報を持たない |
| .kasc 設定 | ◐ GCS 内で秘匿 ログイン後は全量がブラウザへ配信 |
◐ 同左 | ◐ IAP 内側 静的ファイルでよい場合は GCS 不要 |
| HTML/JS/CSS (アプリ本体) |
❌ 全公開 | ✅ 未ログインは 401 ログイン画面系のみ公開 |
✅ IAP がリダイレクト |
| データ(web/) | ❌ 列挙容易 / △ obscurity | ✅ 未ログインは 401 | ✅ IAP 内側 |
| データ(gs:// 経由) | ✅ トークン必須 署名 URL は期限付きベアラ |
✅ 同左(Cookie でも可) | ✅ IAP JWT or トークン |
| /api/gemini | ✅ kasugai トークン or Bearer | ✅ Cookie セッション | ✅ IAP JWT |
| Maps API キー (GOOGLE_MAPS_API_KEY) |
❌ 公開 — capabilities 経由でブラウザへ配布。タイル URL に露出する設計上の公開キーなので、GCP の HTTP リファラ制限で保護 | ||
✅ 秘匿 ◐ 条件付き ❌ 公開 ——「ログインした人」からは .kasc・データは見えます。秘匿が効くのは未ログインの第三者に対してです。署名付き URL は発行時点で認証済みの期限付きベアラ URL なので、有効期間内は誰でも使えます(r2:// の ?token= と同等のリスク)。
セキュリティ上の注意
- トークン形式は Cloudflare 版と同じ
expiry.HMAC-SHA256。秘密鍵はKASUGAI_TOKEN_SECRET(未設定時KASUGAI_AUTH_PASS) - プロセス内レート制限はマルチインスタンス環境ではベストエフォートです。本格運用では外部 HTTPS LB + Cloud Armor を前段に置いてください(run.app 直の構成では Cloud Armor は使えません)
- モード6 の Cookie は HttpOnly・Secure・SameSite=Strict・12時間。モード7 は署名検証用の
KASUGAI_IAP_AUDIENCE設定を推奨(未設定でも IAP 直下では到達自体が認証済みですが、防御の深さとして) - Cloud Run のリクエスト上限は 32MiB です。それを超えるデータアップロードは署名付き URL 直 PUT が必要です(現状の PUT はプロキシ経由)