AI

Cloudflare Workers権限制御入門——AIエージェント用トークンを最小化する

Cloudflare Workersの新しい細粒度権限制御を題材に、AIエージェントやGitHub Actionsへ渡すAPIトークンをWorker単位に絞り、Firestoreへ監査ログを残す実践手順を解説します。

2026年9月21日
Cloudflare WorkersAIエージェントGitHub ActionsFirestoreセキュリティ
Cloudflare Workers権限制御入門——AIエージェント用トークンを最小化する

はじめに

AIエージェントにデプロイや調査を任せ始めると、モデル性能より先に権限設計が効いてきます。

  • ログ調査だけのはずが、Workerのコードまで読めてしまう
  • GitHub Actionsのトークンが、全Workerやドメイン設定まで触れてしまう
  • Preview用の修正エージェントが、本番Workerを変更できる状態になっている
  • 「誰に、どのWorkerへ、どの権限を渡したか」がアプリ側に残っていない

2026年9月15日、Cloudflareは「Give every teammate and agent the right level of access to your Workers」を公開し、Workers向けの細粒度権限制御を発表しました。公式ブログでは、特定のWorkerだけにアクセスを絞り、4つのDeveloper Platformロールで操作範囲を分けられると説明されています。

本記事では、架空の予約管理SaaS「ReserveKit」を題材に、Next.js App Router + Firestore + GitHub Actions + TypeScriptで、AIエージェント用Cloudflare APIトークンをWorker単位に絞る運用を作ります。

参考にした一次情報は次の通りです。

何が変わったのか

CloudflareのWorkers権限ドキュメントでは、権限はRoleとScopeを組み合わせるPermission Policyとして説明されています。ScopeはPlatform、Product、Resourceの3段階です。AIエージェントやCIに渡すなら、基本は「1つのWorkerだけ」を表すResourceスコープに寄せます。

RoleできることReserveKitでの用途
Metadata Read-Only設定、メトリクス、ログ、トレースを見られる。コードやデータは見ない障害調査エージェント
Content Read-OnlyコードやD1行、R2オブジェクトなどを読める。変更はできないレビュー専用エージェント
Editor内容と設定を更新できる。作成や削除はできない既存Workerのデプロイ
Admin作成、削除、アクセス付与を含む完全管理人間の責任者

とくに実務で使いやすいのは、GitHub ActionsにResourceスコープのEditorを渡す構成です。既存Workerへの wrangler deploy には足りますが、Workerの作成や削除はできません。トークンが漏れたり、エージェントの指示がずれたりしても、影響範囲を1つのWorkerに閉じ込めやすくなります。

ハンズオン1: 権限台帳をFirestoreに残す

まず、Cloudflare Dashboardで作成したトークンの「用途」をFirestoreに記録します。トークン値そのものは保存しません。保存するのは、どのWorkerへ、誰が、どのRoleを使うのかという台帳です。

// src/lib/cloudflareAccessPolicy.ts
export type CloudflareRole =
  | "Metadata Read-Only"
  | "Content Read-Only"
  | "Editor"
  | "Admin";

export type WorkerAccessPolicy = {
  workerName: string;
  actor: "human" | "ci" | "agent";
  actorName: string;
  role: CloudflareRole;
  scope: "Platform" | "Product" | "Resource";
  purpose: string;
};

export function validateWorkerPolicy(policy: WorkerAccessPolicy) {
  if (policy.actor !== "human" && policy.role === "Admin") {
    return "CIやAIエージェントにはAdminを渡さない";
  }

  if (policy.actor === "agent" && policy.scope !== "Resource") {
    return "AIエージェントは個別WorkerのResourceスコープに絞る";
  }

  return null;
}

Server Actionでは、管理者だけが台帳を追加できるようにします。db はFirebase Admin SDKで初期化済みのFirestoreクライアント、requireAdminUser は自社アプリ側の認可ヘルパーという前提です。

// src/app/admin/cloudflare-access/actions.ts
"use server";

import { FieldValue } from "firebase-admin/firestore";
import { db } from "@/lib/firebaseAdmin";
import {
  type WorkerAccessPolicy,
  validateWorkerPolicy,
} from "@/lib/cloudflareAccessPolicy";
import { requireAdminUser } from "@/lib/requireAdminUser";

export async function registerWorkerPolicy(input: WorkerAccessPolicy) {
  const user = await requireAdminUser();
  const error = validateWorkerPolicy(input);

  if (error) {
    throw new Error(error);
  }

  await db.collection("cloudflareWorkerAccessPolicies").add({
    ...input,
    createdBy: user.uid,
    createdAt: FieldValue.serverTimestamp(),
  });
}

この台帳があると、PRレビュー時に「このエージェントは本当にこのWorkerだけ触れるのか」を確認できます。Cloudflare側の設定と、アプリ側の運用記録を分けて持つのがポイントです。

ハンズオン2: GitHub Actionsのトークンを分ける

Cloudflare docsでは、CI/CDやエージェントなどの自動化にはAPI tokensを使うと説明されています。また、Wranglerで細粒度権限を使う場合、wrangler login のOAuthフローではなく、アカウント所有のAPIトークンを CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID で渡す流れが案内されています。

ReserveKitでは、次のようにEnvironmentごとにトークンを分けます。

GitHub EnvironmentWorkerRoleScope
previewreservekit-edge-previewEditorResource
productionreservekit-edgeEditorResource
debugreservekit-edgeMetadata Read-OnlyResource
# .github/workflows/deploy-worker.yml
name: Deploy Cloudflare Worker

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: yarn
      - run: yarn install --frozen-lockfile
      - run: yarn build
      - run: yarn wrangler deploy
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

既存Workerへ wrangler deploy するにはWorkerに対するEditorが必要です。一方、存在しないWorkerを新規作成する場合はWorkers product scopeのAdminが必要です。つまり、AIエージェント用CIでは「既存の1 Workerにだけデプロイできる」状態を意図的に作れます。

ログ調査だけなら、Metadata Read-Onlyの別トークンで十分です。

export CLOUDFLARE_API_TOKEN="<METADATA_READ_ONLY_TOKEN>"
export CLOUDFLARE_ACCOUNT_ID="<ACCOUNT_ID>"
yarn wrangler tail reservekit-edge

ハンズオン3: Routes変更を別承認にする

Workerのコード更新と、RoutesやCustom Domainsの変更はリスクが違います。Cloudflare公式ブログでは、RouteやCustom Domainを追加・変更・削除するには、WorkerへのEditorに加えて対象zoneのWorkers Routes権限が必要だと説明されています。

通常のAIエージェント用CIにはRoutes権限を渡さず、PR側でも変更を見つけたら承認ラベルを求めます。

// src/lib/cloudflareRouteGuard.ts
export function requiresRouteApproval(
  files: { filename: string; patch?: string }[],
  labels: string[],
) {
  const routeChanged = files.some((file) => {
    const isWranglerConfig = ["wrangler.json", "wrangler.jsonc", "wrangler.toml"]
      .includes(file.filename);

    return isWranglerConfig && /routes|route|custom_domain|zone_name|zone_id/
      .test(file.patch ?? "");
  });

  return routeChanged && !labels.includes("cloudflare-routes-approved");
}

このチェックはCloudflareの権限を代替するものではありません。レビュー体験を前倒しするためのガードです。最終的には、GitHub Actionsに渡すAPIトークンからRoutes権限を外しておくことが本丸です。

30タスクを回すときの分け方

AIエージェントが入ると、1つの大きな権限で全部を済ませたくなります。しかし、複数タスクを同時に回すときほど、トークンは作業単位で分けた方が安全です。

タスク種別AI/CIに渡す権限バッチサイズ
ログ調査Resource + Metadata Read-Only3〜5件
コードレビューResource + Content Read-Only2〜4件
PreviewデプロイPreview Worker + Editor3件まで
ProductionデプロイProduction Worker + Editor1件ずつ
Route変更通常CIには渡さない個別対応

作業を任せるほど、権限を強くするのではなく、狭く、短く、交換しやすくする。これがAIエージェント時代の基本になります。

まとめ

Cloudflare Workersの細粒度権限制御は、AIエージェントにデプロイや調査を任せる現場で使いやすいアップデートです。

  • Metadata Read-Only、Content Read-Only、Editor、Adminを作業内容で使い分ける
  • CIやAIエージェントには、原則として個別WorkerのResourceスコープを渡す
  • WranglerではAPIトークンを CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID で使う
  • RoutesやCustom Domainsの変更は、通常デプロイとは別の承認対象にする
  • Firestoreに権限台帳を残し、PRレビューで確認できるようにする

AIエージェントに任せる範囲が広がるほど、「何ができるか」より「どこまでしかできないか」が効いてきます。Worker単位の権限制御は、その境界線を現実的に引くための、地味ですが強い道具です。