本文へ移動
Overview · Architecture

全体クラウド構成One-page architecture

11 個の手順で構築したリソースが、本番環境でどう連動するかを 1 枚の図にまとめました。「ユーザのアクセス → 入口(LB + WAF)→ 4 つの Cloud Run サービス → ストレージ / DB / メッセージング基盤 → Google Drive ミラー」の流れを、上から下に追えるように配置しています。

01

ランタイム構成Runtime topology

ユーザがブラウザでアクセスしてから、データベース・Google Drive へ届くまでの主要経路です。teal の太線は同期的な主経路、灰色の細線は非同期処理、点線はスケジュール起動を示します。

図 01 · 松葉 / Matsuba のランタイム構成(本番)
社内ユーザ(情シス / 部門)Google SSO · @example.com社外ユーザ(取引先・委託先)メール + パスワード · OTP MFA 必須共有リンク受取人ログイン不要 · inbox.portal.example.comCloud Load Balancer + Cloud Armor(WAF)TLS 終端 · 2 ホスト振り分け(portal.example.com / inbox.portal.example.com) · OWASP 6 種 + IP レート制限CLOUD RUN · ASIA-NORTHEAST1(TOKYO)matsuba-webNext.js 16 · UImin=1 / 上限を設定matsuba-apiHono · API + 認証min=1 / 上限を設定matsuba-syncGo · Drive 同期min=0 / 上限を設定matsuba-scannerGo + ClamAV · ウイルス検査min=1 / 上限を設定 / 大きめのメモリmatsuba-batchRun Jobs · 定期処理17 cron / アイドル 0 円fetchINFRASTRUCTURE · TOKYOSupabase PostgresDB のみ · RLS · PITRPro 東京Cloud Storage(5 バケット)未検査 / 検査済み / 隔離ミラー(ウイルス定義) / 共有 ZIPCloud Pub/Sub13 topic + 13 DLQ22 subscriptionEventarcGCS → Scanner未検査バケット監視Resend招待 · 通知 · OTPmail.example.comPLATFORM · GLOBALSecret Manager7 シークレット · SA 別の最小権限Artifact Registryコンテナイメージ保管Cloud Scheduler + Tasks17 cron · 1 メールキューCloud Monitoring + Billingアラート · 月予算Google 共有ドライブ(同期先)Box 単位の片方向ミラー · appProperties で突合PushDrive API · 片方向Scheduler が batch を起動

読み方: teal の太線は「ファイルが感染していないとわかった瞬間に Drive 同期を起動する」など、業務上クリティカルな同期主経路です。灰色の細線は Pub/Sub やバッチを介した非同期、点線(金茶色)はスケジュール起動を示します。matsuba-sync と matsuba-batch は需要が無いとき 0 インスタンス(課金 0 円)に縮退します。

02

本構築で作成したクラウドリソース一覧Resource manifest

手順 00〜10 で作成したものを、層別に並べました。それぞれの設定詳細は対応する手順ページに展開しています。

層サービス件数役割
入口 / 公開境界Cloud Load Balancing1 LB / 4 バックエンド / 2 NEG2 ホスト振り分け、1 枚の SAN 証明書で TLS 終端
入口 / 公開境界Cloud Armor2 ポリシー × 7 ルールOWASP 6 種 + IP ごとのレート制限(本体と共有リンクで別のポリシー)
入口 / 公開境界Cloud DNS + Cloud Domains1 ゾーン / 2 A レコードportal.example.com + inbox.portal.example.com
実行基盤Cloud Run(Service)4 サービスweb / api / sync / scanner、東京リージョン
実行基盤Cloud Run(Jobs)1 ジョブ + 17 スケジュール(予定)定期バッチ統合、アイドル時 0 円
実行基盤Artifact Registry1 リポジトリコンテナイメージ保管、一定期間・一定数を保持する自動掃除
メッセージングPub/Sub13 主トピック + 13 DLQ / 22 サブスクリプション業務イベント配信 + 失敗時退避先
メッセージングEventarc1 トリガ未検査バケットの新規 → scanner サービスへ Push
メッセージングCloud Tasks1 キューメール送信ジョブの非同期化
ストレージCloud Storage5 バケット未検査 / 検査済み / 隔離 / ミラー(ウイルス定義) / 共有 ZIP
ストレージSupabase Postgres1 プロジェクト東京 Pro、PITR + 3 拡張(pg_trgm / uuid-ossp / pgcrypto)
認証 / アイデンティティサービスアカウント7 個web / api / sync / scanner / batch(Jobs)の各実行 SA・隔離読取専用 SA・デプロイ用 SA
認証 / アイデンティティWorkload Identity Federation1 プール / 1 プロバイダGitHub Actions からのキーレス認証
認証 / アイデンティティGoogle OAuth Client1 クライアントBetter Auth の Google ログイン
認証 / アイデンティティGoogle 共有ドライブ1 ドライブ「社内アーカイブ」、sync の実行 SA を Content manager 追加
シークレットSecret Manager7 シークレットDB / Better Auth / OAuth / Resend / CORS、SA 単位の最小権限
外部 SaaSResend1 API キー + 1 Webhookドメイン mail.example.com を再利用、件名/送信者名でブランド明示
監視 / コストCloud Monitoring1 通知チャネル + 1 アラート5xx 検出を起点に Step 99 で詳細化
監視 / コストCloud Billing1 月予算予算に近づいたとき・達したとき・超えたときの 4 段階で通知
監視 / コストCloud Error Reporting新規エラー通知 ONCloud Logging から自動集約
CI/CDGitHub リポジトリ + Secrets1 リポジトリ + 5 Secrets<owner>/matsuba(Private)

合計: 上記の構成要素は 主要だけで 150 以上。一見多いように見えますが、これらは全て「設計ルール(本体の設計文書)に書かれた構造をそのまま再現したもの」で、コンソール上の手作業で構築しています。

03

公開境界の方針Public boundary

社外取引先がアクセスする前提のため、IAP(Identity-Aware Proxy = Google アカウント縛りのアクセス制御)は使えません。代わりに 5 層の多層防御で守ります。

層守るもの守り方
外側Cloud Armor(WAF)OWASP 6 種(SQLi / XSS / LFI / RFI / RCE / Scanner Detection)+ IP ごとのレート制限(本体と共有リンクで別のポリシー)
2 層目Cloud Load BalancingHTTPS 終端、HSTS ヘッダ送出(旧クライアント保護)、ホスト名で portal.example.com と inbox.portal.example.com を振り分け
3 層目apps/web の proxy.tsBetter Auth のセッションを毎リクエスト検証、未ログインは /sign-in に強制リダイレクト
4 層目apps/api ミドルウェアCookie / Bearer 検証、Origin チェック、データベースに送る JWT を生成
最内Postgres RLS行レベルセキュリティ(DB が「あなたが見られる行か」を最終判定。アプリにバグがあっても、この判定が最後の防壁になる)
用途ホスト役割
本体アプリportal.example.com認証必須の全機能(サインイン、Box 操作、管理画面、API)
共有リンク受取inbox.portal.example.com受取人向け(ログイン不要)。本体 API は露出させない
04

サービスアカウントの配置Service account topology

サービスアカウント(SA)は「アプリが Google Cloud にアクセスする際の身分証」です。用途別に分け、必要なリソース(バケット / トピック / シークレット単位)にだけ最小限の権限を与えています。JSON 鍵ファイルは 1 つも発行していません。

SA用途主な権限
web の実行 SAmatsuba-web(Next.js UI)apps/api を内部呼び出し
api の実行 SAmatsuba-api(業務 API)GCS(未検査 / 検査済み / 共有 ZIP の各バケット)、Pub/Sub publish、Secret Manager 参照、Cloud Tasks enqueue
sync の実行 SAmatsuba-sync(Drive 同期)検査済みバケット read、Pub/Sub subscribe、共有ドライブの Content manager(fileOrganizer)
scanner の実行 SAmatsuba-scanner(ウイルス検査)4 バケット R/W(未検査 / 検査済み / 隔離 / ミラー(ウイルス定義))、Pub/Sub publish、Eventarc 受信
batch(Jobs)の実行 SAmatsuba-batch(定期処理)GCS、Postgres、Pub/Sub、Secret Manager
隔離読取専用 SA感染ファイル取り出し専用(system_admin が impersonate)隔離バケットのみ Viewer。誰にも紐付けず、必要時のみ手動で借りる
デプロイ用 SAGitHub Actions のデプロイ用Cloud Run admin、Artifact Registry writer、Secret Manager 参照

意図: api の実行 SA に 隔離バケットへの権限を与えていないのは、「業務 API が誤って感染ファイルを取り出す経路をそもそも作らない」ためです。隔離ファイルへのアクセスは専用の 隔離読取専用 SA を一時的に借りる形でしか行えません。