docs.bizlp.dev(社内ドキュメントサイト)と docs.bizlp.dev/api/*(docs-search-worker:検索 / RAG / inject.js 配信)を、Cloudflare Access の Entra ID (Office 365) ログインで保護するためのダッシュボード作業手順。

リポジトリ側のコード対応(worker の Basic 認証撤去・Worker Route 設定・GitHub Actions の CF Access service token 切替)は PR1 で完了済み。本書は Cloudflare / Azure ダッシュボードの手作業(ユーザー担当)の手順。完了すると https://docs.bizlp.dev/ が Entra ID ログインで開けるようになる。

認証の仕組み(実装済みの挙動)

  • docs サイト本体(CF Pages)と検索 API(docs-search-worker)は 同一ホスト docs.bizlp.dev に同居する。
    • /api/* だけ Worker Route で docs-search-worker に振り、それ以外は Pages がドキュメントを配信する。
  • Access ポリシーは docs.bizlp.dev 1 個に対して掛ければ、ページ・worker の両方をまとめて保護できる。
  • ブラウザは同一オリジン fetch で Access cookie を自動添付するため、worker 側で JWT 検証コードを持たない(エッジ完結)。
  • headless ingest(scripts/docs-vectorize.mjs.github/workflows/docs-vectorize.yml)は CF Access service token で通過する。

手順

1. Zero Trust の有効化(既に OCR Bench で完了済なら 4. に進む)

Cloudflare ダッシュボード → Zero Trust → 初回オンボーディングで team 名 を決める(既に bizlp を採用済 → team domain は bizlp.cloudflareaccess.com)。Free プラン(50 ユーザーまで)で可。

2. Entra ID 側:IdP として OCR Bench と同じものを再利用

ocr/docs/cf-access-setup.md で登録済の Entra App と Cloudflare 側 IdP 設定を そのまま再利用 する。docs 用に別 App を切る必要はない(同一 tenant・同一 Cloudflare team domain のため)。

未登録の場合のみ、ocr/docs/cf-access-setup.md §2-3(Azure App registration → Cloudflare IdP 登録)を先に完了させる。

3. CF Pages カスタムドメインの追加

Cloudflare ダッシュボード → Workers & Pages → bizlp-gas-accounting Pages プロジェクト → Custom domains → Set up a custom domaindocs.bizlp.dev を入力 → Continue。

  • DNS は同じアカウントの bizlp.dev ゾーンに自動で CNAME(proxied)が作成される。
  • 数分で証明書がプロビジョニングされる。完了後 https://docs.bizlp.dev/ がドキュメントの index にリダイレクトされる(この時点ではまだ Access 未保護で誰でも見られる)。

4. Worker Route の追加(/api/* → docs-search-worker)

docs-search-worker/wrangler.toml には既に下記の routes が定義済み:

routes = [
  { pattern = "docs.bizlp.dev/api/*", zone_name = "bizlp.dev" }
]
workers_dev = false

PR1 の main merge で .github/workflows/deploy-docs-search-worker.yml が自動デプロイを走らせるため、merge した時点で Worker Route が反映される(手動ダッシュボード作業は不要)。

deploy 後、https://docs.bizlp.dev/api/health が JSON を返せば Route 反映完了の目安。

5. Access アプリケーション作成

Zero Trust → Access → Applications → Add an application → Self-hosted

  • Application name: bizlp docs
  • Session duration: 24h 推奨(OCR Bench に揃える)
  • Application domain: docs.bizlp.dev(path は空 = 全体保護。/api/* も含めて 1 ポリシーで覆う)
  • Identity providers: 手順 2 の Entra ID のみ ON(One-time PIN は OFF 推奨)
  • Policies(2 つ作成):
    1. Allow human users(Action = Allow)
      • Include → Emails → 許可するメールアドレスを列挙([email protected] ほか)
    2. Allow ingest service token(Action = Service Auth)
      • Include → Service Token → 手順 6 で発行するトークンを選択
  • 作成後、アプリの Audience (AUD) タグ を控える(Overview に表示。Worker 側で JWT 検証はしないため必須ではないが、将来コード側検証を入れる場合に必要)。

6. Service Token の発行(ingest 用)

Zero Trust → Access → Service Auth → Service Tokens → Create Service Token

  • Name: docs-search-vectorize-ci
  • Duration: 1 year(運用ルールで翌年 rotate)
  • 作成後の Client IDClient Secret を即控える(Secret は再表示不可)。

控えた 2 値を Allow Service Tokens ポリシー(手順 5)に紐付けるのを忘れずに。

7. GitHub Secret 投入

# 控えた値を投入
gh secret set DOCS_SEARCH_CF_ACCESS_CLIENT_ID --body "<Client ID>"
gh secret set DOCS_SEARCH_CF_ACCESS_CLIENT_SECRET --body "<Client Secret>"

.github/workflows/docs-vectorize.yml がこの 2 つを scripts/docs-vectorize.mjs に渡し、/api/ingest を service token 経由で叩く。

旧 secret の掃除(PR2 で実施予定): DOCS_SEARCH_AUTH_USER / DOCS_SEARCH_AUTH_PASS は不要になるため、運用が安定したら gh secret delete する。

8. 動作確認

  1. シークレットウィンドウで https://docs.bizlp.dev/ → Entra ID ログイン画面 → ログイン後ドキュメント index が表示される。
  2. ポリシー外のアカウントでログイン → Access がブロックする。
  3. https://docs.bizlp.dev/api/health → Access 経由でログイン → {ok: true, ...} JSON。
  4. docs ページ内の 🔍 ボタンで検索 → 結果が返る(Authorization ヘッダ無しでも通る)。
  5. gh workflow run docs-vectorize.yml → 全 batch が 200 で完了する。

9. (任意).pages.dev 直叩きの遮断

https://bizlp-gas-accounting.pages.dev/ は Pages の標準 URL として残るため、放置すると認証を素通りする裏口になる。Zero Trust → Access → Applications で bizlp-gas-accounting.pages.dev 用に Block 専用の Self-hosted アプリ を 1 つ追加して塞ぐ(Allow policy 無し)。

トラブルシュート

  • /api/inject.js の script タグが 302 → ログイン画面 HTML を返してきて検索 UI が出ない: ブラウザがまだ Access cookie を持っていない(初回アクセス時)。docs サイト本体に先にアクセスしてログインを通せばよい。両者が同一オリジンなので、ページ読込が完了した時点で cookie は確実に揃っている(→ 通常運用で発生しない)。
  • gh workflow run docs-vectorize.yml が 401/403 で fail: service token の Client ID/Secret が gh secret に正しく入っているか、Access アプリのポリシーに該当 token が紐付いているかを確認。
  • Pages preview URL(<hash>.bizlp-gas-accounting.pages.dev)で検索が動かない: preview pages は Access 範囲外 + 異なるホストのため、docs.bizlp.dev/api/* への fetch が cross-origin かつ cookie 無しになり 302 で失敗する。Preview の検索機能は仕様上断念(本番 docs.bizlp.dev で検証する)。
  • .pages.dev 直叩きで認証を素通りされる: 手順 9 の Block アプリで塞ぐ。

関連

  • 既存の OCR Bench 版手順(Worker 側 JWT 検証コードを含む完全版): ocr/docs/cf-access-setup.md
  • DRP の同種事例: ADR-0110 / drp/docs/cf-access-setup.md
  • Worker Route 定義: docs-search-worker/wrangler.toml
  • ingest スクリプト: scripts/docs-vectorize.mjs / .github/workflows/docs-vectorize.yml