型付き辺: 出 4 / 入 0
ADR-0110: Decision Pipeline の内部 Basic 認証を Cloudflare Access service token に置換する
- Status: Accepted
- Mode: Standard
- Kruchten Type: Executive/Property
- Scope: platform
- Implementation Status: Not Started
- 起案者: [email protected]
- 起案日時 (JST): 2026-06-03 23:36
- 承認日時 (JST): 2026-06-16
- Deciders: [email protected] (単独)
コンテキスト
§1.1 背景
Decision Pipeline の内部 Worker (decision-pipeline.t-saitoh.workers.dev) は現在 Basic 認証 (AUTH_USER / AUTH_PASSWORD) で保護されており、資格情報が macOS Keychain / GitHub Secret / 各呼び出し元 env と複数箇所に静的長寿命シークレットとして散在している。RQ-085 の 3 モデル Deep Research (Claude / Gemini / OpenAI) は 3 者一致で「内部 Basic 認証は Cloudflare Access service token に置換」を推奨 (docs/research/rq-085-auth-secret-consolidation.md §3.1-4) しており、本 ADR はその推奨 #3 を確定するものである。
§1.2 現状 (As-Is)
- Basic 認証資格情報は (a) macOS Keychain (
decision-pipeline-auth)、(b) CI の GitHub Secret、(c) 各呼び出し元の env と 3 箇所に静的長寿命シークレットとして散在。 - Worker 内に Basic 認証ミドルウェアが実装され、認証判定は Worker プロセス内で完結している。
- 監査 API (
/drafts等) は sub から Keychain Basic 認証経由で叩く構成。
§1.3 課題
- 失効の事前検知が無い: Basic 認証は「予告なく失効する」資格情報の典型で、破綻して初めて気づく。実際このセッションでも
ghトークン失効で git pull が止まり、復旧に手動再認証を要した。同種の静的シークレット失効が pipeline 起案フローを無告知で止めるリスクが恒常的に存在する。 - sub サンドボックスからの疎通が脆い: 監査 API は sub から Keychain Basic 認証経由で叩けるが、Keychain 依存のため実行環境 (headless / cron / 別クローン) によって疎通可否がぶれる ([[project_sub_cannot_reach_audit_api]] で実際に「復号不可」と誤認した経緯あり)。
- rotation・blast radius 管理が手作業: Basic 認証は scope 分離も期限も持たず、漏洩時の影響範囲を機械的に絞れない。
§1.4 制約・要件
- 1 人法人で追加金銭コストを発生させない (Cloudflare Zero Trust Free プラン 50 users 内に収める)。
- CI・ローカル・sub の全呼び出し元から疎通可能であること。
- Wrangler / Cloudflare ネイティブ機構で保守対象コードを増やさないこと。
- 失効を破綻前に検知できる仕組みを持つこと。
- Decision Pipeline のグラフ構造・triage 基準・telemetry スキーマは変更しない (ADR-0102 の排他的除外リスト対象外)。
§1.5 目標 (To-Be)
Decision Pipeline Worker 前段の認証を Cloudflare Access service token に集約し、静的長寿命シークレット 0・失効事前検知あり・全呼び出し元疎通安定の状態を実現する。Non-Goals: 静的 secret 単一ソース化 + GitHub OIDC/WIF + pre-flight 失効検知 (残テーマ③) は本 ADR スコープ外で別途 DOC-OPS-18 として起案する。
決定
Decision Pipeline Worker の前段保護を Cloudflare Access の Service Auth ポリシー + service token (CF-Access-Client-Id / CF-Access-Client-Secret ヘッダ) に置換する。Worker 内の Basic 認証ミドルウェアは撤去し、認証判定を Cloudflare エッジ (Access) に集約する。CI・ローカル・sub の各呼び出し元はヘッダ 2 本を送るだけにし、token は Cloudflare 側で一元管理・期限管理・失効前アラートを受ける。移行は Basic 認証と Access の一時併存方式で段階的に行う。
判断基準 (Decision Drivers)
3.1 評価軸
| # | 軸 | 重要度 (係数) | 案件特有の解釈 |
|---|---|---|---|
| 1 | #secure | Must (×2.0) | 静的長寿命シークレットの撲滅 + scope/期限を持つ資格情報への移行 |
| 2 | #operable | Must (×2.0) | CI・ローカル・sub の全呼び出し元から疎通でき、失効を破綻前に検知できる |
| 3 | #maintainable | High (×1.0) | Wrangler / CF ネイティブ機構で保守対象コードを増やさない |
| 4 | #efficient | Medium (×0.5) | 1 人法人で追加金銭コストが発生しない |
K.O. criterion: Must 軸 (#secure / #operable) のスコアが 3 未満の案は不採用。
3.2 評価軸 × 案スコア表
各案 0-5 で評価。加重和 = (Σ score × 係数) / (満点 × Σ 係数) で正規化 (0.0-1.0)。
| 軸 | 係数 | 採択案 (CF Access service token) | 案 A (Basic 維持) | 案 B (mTLS) |
|---|---|---|---|---|
#secure (Must) | ×2.0 | 5 | 2 | 5 |
#operable (Must) | ×2.0 | 4 | 3 | 2 |
#maintainable (High) | ×1.0 | 4 | 3 | 2 |
#efficient (Medium) | ×0.5 | 5 | 5 | 3 |
| 加重和 (正規化) | 0.886 | 0.586 | 0.643 | |
| K.O. 通過 (Must ≥3) | ✓ | ❌ (#secure=2) | ❌ (#operable=2) |
検討した代替案 (Alternatives Considered)
- 案 A (Basic 認証維持 + rotation 強化): 現行 Basic 認証を維持し rotation 自動化を追加 — 静的長寿命シークレットが残り
#secureで K.O.、失効事前検知も得られないため不採用。 - 案 B (mTLS によるクライアント証明書認証): Worker 前段で mTLS を要求 — Cloudflare Workers での mTLS 対応や sub サンドボックスでの証明書配布運用負荷が高く
#operableで K.O.、保守対象も増加するため不採用。 - 案 C (採用案: Cloudflare Access service token): 認証判定をエッジに集約、ヘッダ 2 本で全呼び出し元統一、Free プラン内・rotation/期限管理を CF 側に委任。
影響 (Consequences)
§5.1 正の影響 (Good)
- 静的長寿命 Basic 認証シークレットが 0 になり、失効はエッジで事前検知される。
- Worker から Basic 認証ミドルウェアが消え保守対象が減る。
- sub の監査 API 疎通が Keychain 非依存で安定する。
- Cloudflare Zero Trust Free プラン内 (月 $0) で完結。
§5.2 負の影響 (Bad)
- Cloudflare Access の application/policy という管理対象が 1 つ増える。
- service token の rotation 運用 (年次) が新たに発生。
- 認証判定をエッジに委譲するため、Worker 自体は生きていても認証レイヤーだけが死ぬ新種障害モードが発生し得る。
§5.3 中立・トレードオフ (Neutral / Trade-offs)
- Cloudflare Access コントロールプレーン障害時の全呼び元同時疎通断: Basic 認証は Worker 内ミドルウェアで完結するため Cloudflare 内部障害でも Worker 自体は稼働するが、Access service token 方式では Cloudflare Access コントロールプレーン障害・設定伝播遅延発生時に CI / sub / ローカルの全呼び出し元が同時に 401/403 を受け取り pipeline が停止する。2023-09 Zero Trust 大規模障害など実績あり。緊急フォールバック用環境変数フラグの実装方針および Cloudflare statuspage RSS の監視組み込みで緩和する。
- rotation 属人化リスク: 年 1 回 ~10 分作業は手順を忘れやすく、14 日アラートが他タスクに埋もれて先送りされる懸念。Runbook 文書化 + 30 日/14 日 2 段階アラート + pre-flight 自動検証で緩和。
- Client Secret 平文露出リスク:
curl -vや Wrangler verbose、console.log(headers)で Secret がログ出力される懸念。CI でadd-mask必須化 + pre-commit hook で-v/console.log(headers)パターン検出で緩和。 - 設定 Drift リスク: GUI 操作のみで Access application / policy / service token を作成すると変更履歴がコードリポジトリに残らず、誤操作時のレビューが効かない。最低限スクリーンショット + wrangler.toml 対応表を docs/ にコミット、余力があれば Cloudflare Terraform provider で IaC 化を推奨。
- Free プラン条件変更リスク: Cloudflare の Free 枠条件改定 (service token が user 枠計上に再定義される等) で予期しない課金が発生する可能性。月次請求確認を ops チェックリストに追加し、$0 超過時は Basic 認証ロールバックを含む代替案評価のトリガーとする。
- LLM 推奨の独立性: 3 モデル一致推奨は同一学習分布からの相関出力であり独立した証拠ではない。確証バイアス回避のため移行 2 週間後の振り返りセッションを設定する。
コスト試算
- 実装: ~0.75 人日 (AI 支援あり: Access application + Service Auth policy + service token 作成 ~0.25 + Worker 認証ミドルウェア撤去 ~0.25 + 呼び出し元ヘッダ切替/段階移行 ~0.25)。
- 運用: Cloudflare Zero Trust Free プラン内で 月 $0、rotation 年 1 回 ~10 分。
- 類似実績 [[adr-0104]] (GAS/clasp デプロイ認証) より見積もり。
撤退条件 (Rollback Plan)
- 移行後 4 週間以内に、CI / sub からの監査 API 呼び出し失敗率が 1% を超え、かつ 24 時間以内に原因特定できない場合、Basic 認証併存にロールバックして切り分け (盲点検出 #4 を踏まえ提案書の 5% から 1% に引き下げ)。
- 移行は Basic と Access の一時併存方式で行い、ロールバックは Worker 側 Basic 認証ミドルウェア再有効化 ~15 分で可能。
- 移行後に Cloudflare Access 起因の疎通断が月 1 回以上発生したら案 A (Basic 維持) を再評価。
- Cloudflare Free プラン条件変更で月額が $0 を超過した場合、即座に Basic 認証ロールバックを含む代替案評価をトリガーする。
Confirmation
- 検証手段:
- CI の監査 API 疎通ステップの成功率を集計。デプロイ前 pre-flight で service token の有効性 (200 応答) と失効までの残日数をログ出力。
- Basic 認証由来シークレットがリポ/CI 設定に残存しないことを
gh secret list+ grep で確認。 - CI では
CF_ACCESS_CLIENT_SECRETをadd-maskで即時マスク登録するステップを必須化。pre-commit hook で-vフラグやconsole.log(headers)パターンを検出。Wrangler / curl の verbose フラグを本番フローから除外。 - Cloudflare Access コントロールプレーン障害に備え、緊急時に Worker 側で認証をスキップできる環境変数フラグ (緊急スイッチ) を実装し、Cloudflare statuspage の RSS を監視 pipeline に組み込む。
- rotation 手順 (旧 token 削除 → 新 token 発行 → 全呼び出し元への配布 → CI Secret 更新 → sub env 更新 → 動作確認) を Runbook として docs/ に定義し、本 ADR の参照節からリンクする (Runbook は実装フェーズ成果物)。rotation 完了は CI pre-flight で新 token 200 応答を自動検証。
- Access application / policy / service token の作成時スクリーンショットと wrangler.toml の対応表を docs/ にコミットし、Drift 検出の基準とする。
- 月次 Cloudflare 請求確認を ops チェックリストに追加。
- 移行 2 週間後に振り返りセッションを実施し、LLM 推奨を除いた論拠の妥当性を再評価。
- 実行頻度: PR 毎 (CI 疎通・mask・pre-commit) / 月次 (token 残日数・Cloudflare 請求・statuspage 履歴確認) / 年次 (rotation Runbook 実行)。
- 違反時対応: 疎通失敗で pipeline を意図的に fail させ GitHub issue を自動起票。残日数 30 日で issue 起票・14 日でエスカレーション の 2 段階アラート (盲点検出 #2 を踏まえ単発 14 日から 2 段階化)。Cloudflare statuspage で Access 障害検知時は緊急スイッチ手順の判断を起動。
- KPI:
- 監査 API 呼び出し成功率 ≥ 99% (現行 Basic 認証実績を下限の根拠とする)。
- Basic 認証由来の静的シークレット数 = 0 (移行完了時)。
- 失効起因の無告知断 = 0 件/四半期。
未解決事項 (Open Questions) — Accept 前判断
2026-06-16 Accept 時点の解決: 下記 OQ を以下のとおり解決し、本 ADR を Accepted に昇格した。
- OQ-1 → 解決:
*.workers.dev直下には Access を適用できないため、bizlp.devゾーンの独自ドメインdecision-pipeline.bizlp.devを本 ADR の前提とする。同型構成 (独自ドメイン + CF Access(Entra) + workers.dev Basic フォールバック) は OCR workerocr-bench.bizlp.devで本番実証済みであり、移行コストは小 (OCR の wrangler 設定・src/access.tsを流用)。- OQ-2 → 解決: 緊急フォールバックは「認証スキップ環境変数フラグ」を採らず IP allowlist への一時切替 とする (バックドア化・戻し忘れリスクを排除)。人間ユーザーには service token を配布せず Cloudflare Access の user 認証 (Entra SSO / One-Time PIN) を用い、service token は CI・sub 等の機械クライアント専用とする。機械クライアントの service token JWT は
common_name(client ID) を持つため、Worker 側の検証は email/common_name の両対応とする。- OQ-3 → 解決: CF Access は 社内フェーズ限定の解 として採用する。顧客向け製品は別エンドポイント + アプリ層マルチテナント Entra OIDC (将来の Phase 2 ADR) とし、内部 Worker は内部用として CF Access を継続するため二重投資にはならない (ADR-0121 Phase 1 が顧客 PoC を「別 Worker + 別 KV/D1」と既定済みで、内部 Worker は顧客向けにならない)。製品フェーズのテナント仕分け・管理者の他社データ閲覧 (break-glass) は本 ADR のスコープ外で別途 Phase 2 ADR とする。
- OQ-1:
*.workers.dev直下への Access 適用可否 (カスタムドメイン前提): o3 レビュー指摘により、Cloudflare Access の application は zone 配下のカスタムドメイン (自己管理ドメインのホスト名) のみを対象にでき、*.workers.dev直下のホスト名には適用できない可能性がある。現エンドポイントdecision-pipeline.t-saitoh.workers.devのままでは本 ADR の設計前提が不成立となり得るため、実装着手前に main ワークスペースで実機検証して確定する。適用不可と確定した場合は「zone 配下カスタムドメインへの移行」を本 ADR の前提条件に昇格し、移行コストを §コスト試算 に反映する。 - OQ-2: 緊急スイッチ (認証スキップ環境変数フラグ) の再設計: Confirmation 検証手段 4 の「Worker 側で認証をスキップできる環境変数フラグ」は、Gemini / Claude のレビューで「事実上のバックドアであり、フラグの誤設定・戻し忘れが恒久的な無認証公開につながる」と警告された。緊急時フォールバックは認証スキップではなく IP allowlist への一時切替 方式へ再設計することを推奨する。あわせて、ローカルの人間ユーザーには service token を配布せず Cloudflare Access の user 認証 (SSO / One-Time PIN) を用いる構成とする (service token は CI / sub 等の機械クライアント専用)。
- OQ-3: 製品化フェーズの認証方式移行を見据えた過剰投資の回避: 2026-06-04 判明 — Decision Pipeline は製品化後に顧客 (Microsoft 365 基盤) が直接利用する予定であり、顧客向け認証は他プロダクト (JTBD-012 証憑管理・管理会計プロダクト) と共通のマルチテナント Entra ID OIDC (アプリ層実装) が固定制約となる。Cloudflare Access は workforce (自社テナント単位) 向け設計であり、顧客テナントが多数になる CIAM 用途には適さないため、本 ADR の構成は社内フェーズ限定の解であり、認証は Basic → CF Access → アプリ層 OIDC と 2 段移行する前提になる。Accept 前に次の 2 点を判断する: (a) CF Access 周辺の作り込み (OQ-2 の緊急スイッチ再設計・rotation Runbook 等) を社内フェーズで回収できる範囲に留める、(b) 製品化時期が十分近い場合は「CF Access をスキップして最初からアプリ層 OIDC を実装する」案との比較。なおコンピュート基盤は CF Workers のままで顧客向けマルチテナント OIDC を実装可能であり、顧客の M365 基盤が制約するのは ID 面とデータ取込面のみ (基盤移行は本 OQ のスコープ外)。
参照 (References)
- 関連 ADR: [[adr-0104]] (GAS/clasp デプロイ認証の組織内 OAuth クライアント化 — 同じ RQ-085 から分割された姉妹テーマ①)、ADR-0102 (排他的除外リスト)
- 関連 PR/Issue: DOC-OPS-18 (残テーマ③: 静的 secret 単一ソース化 + GitHub OIDC/WIF + pre-flight 失効検知、別途起案予定)
- 外部資料:
docs/research/rq-085-auth-secret-consolidation.md§3.1-4 (3 モデル Deep Research 推奨 #3)、Cloudflare Zero Trust Free プラン (50 users 公式値)、[[project_sub_cannot_reach_audit_api]] - 実装フェーズで追加: service token rotation Runbook (docs/ 配下のパス確定後に本節へリンクを追加する)