읽기 전용 Cloudflare Worker AI 보안 콘솔 구축

작성자

카테고리:

← 피드로
DEV Community · Mike Anderson · 2026-08-25 개발(SW)

Why build this

Security teams already have WAF events, bot signals, access logs, and SIEM pipelines. The problem is not always data collection. The problem is turning that data into a fast, readable operational view without giving a model unsafe authority.

This implementation uses a Cloudflare Worker as the control layer and Workers AI as the summarization layer. The Worker is deliberately read-only.

It supports four workflows:

  • Natural language security queries.
  • Approved query catalogue for sanctioned analyst questions.
  • AI Security Posture Digest.
  • Ray ID / Request Investigator.

For staging examples, I will use example.com.dev. Do not treat that as a real environment.

Design principle

The model should not be the administrator.

The Worker owns:

  • Authentication boundary.
  • Zone allowlist.
  • Fixed GraphQL queries.
  • Secret handling.
  • HTML rendering.
  • Audit logging.

Workers AI owns:

  • Intent classification when deterministic matching is not enough.
  • Security posture summarization.
  • Ray ID explanation.

That split matters. If the model is allowed to create arbitrary queries or perform Cloudflare write actions, the tool becomes much harder to govern.

High-level architecture

flowchart TD
    A[Security analyst] --> B[Cloudflare Access]
    B --> C[sentinel-cf Worker]
    C --> D[Scope and input validation]
    D --> E[Cloudflare Analytics GraphQL]
    D --> F[Workers AI via AI Gateway]
    E --> G[Normalized metrics]
    F --> H[Summary or explanation]
    G --> I[HTML report]
    H --> I
    C --> J[Workers KV latest digest]

Enter fullscreen mode Exit fullscreen mode

What the Worker can do

Workflow Endpoint Output Main menu / HTML Natural language query /query HTML table/cards with raw JSON toggle Approved query catalogue /allowed-queries HTML list with Copy and Use actions Security digest /digest?range=7d HTML report Latest scheduled digest /digest/latest HTML report from KV Ray ID investigation /ray HTML investigation report Health check /healthz JSON

What the Worker must not do

This implementation should not:

  • Block IPs automatically.
  • Create WAF rules.
  • Disable managed rules.
  • Change Access policies.
  • Query arbitrary zones.
  • Store secrets in source code.
  • Treat AI output as formal incident evidence.

The right production pattern is: AI recommends, humans approve, Terraform or approved change control applies.

Required Cloudflare components

You need:

  • Cloudflare Workers.
  • Cloudflare Access.
  • Workers AI binding named AI.
  • AI Gateway.
  • Cloudflare Analytics GraphQL access.
  • Workers KV namespace for scheduled digest storage.
  • Optional Cron Trigger for scheduled reports.

Cloudflare documents Worker deployment through Terraform using cloudflare_worker, cloudflare_worker_version, and cloudflare_workers_deployment. Worker version modules should use content_file where practical to avoid storing large Worker code directly in Terraform state. See Cloudflare Workers IaC.

Runtime configuration

Use these Worker bindings and variables:

Type Name Example Workers AI binding AI Workers AI Catalog KV binding DIGEST_KV sentinel-cf-uat-digests Secret CF_ANALYTICS_TOKEN Redacted Plain variable APP_HOSTNAME sentinel-cf.example.workers.dev Plain variable AI_GATEWAY_ID sentinel-cf-gateway Plain variable ALLOWED_ZONE_TAGS Zone IDs, comma-separated Plain variable ENVIRONMENT uat Plain variable DEFAULT_DIGEST_RANGE 7d Plain variable DIGEST_TITLE SENTINEL-CF Security Posture Digest

ALLOWED_ZONE_TAGS must contain Cloudflare Zone IDs, not domain names such as example.com.dev.

Console implementation first

For UAT, I prefer building the first version in the Cloudflare console. The console path makes it easier to prove each moving part before Terraform becomes the source of truth.

The practical order is:

  • Create the read-only analytics token.
  • Bootstrap the Worker.
  • Protect the Worker with Cloudflare Access.
  • Add Workers AI.
  • Add KV.[Key<>Value pair.]
  • Add runtime variables and secrets.
  • Deploy and test the Worker.
  • Then codify the working design with Terraform.

1. Create the Analytics Read token

In Cloudflare, create a dedicated API token for this Worker:

Manage account -> Account API Tokens -> Create Token -> Create Custom Token

Enter fullscreen mode Exit fullscreen mode

Use the minimum read-only permission required by the Analytics GraphQL API:

Permission group: Analytics
Permission: Read
Scope: Selected zone only
Zone: example.com.dev

Enter fullscreen mode Exit fullscreen mode

Do not grant WAF edit, DNS edit, Access edit, Rules edit, or account administrator permissions.

Store the value as:

CF_ANALYTICS_TOKEN

Enter fullscreen mode Exit fullscreen mode

You will add it to the Worker as a secret later. Do not paste it into the Worker JavaScript.

2. Create or bootstrap the Worker

In Cloudflare:

Workers & Pages -> Create Worker -> Start with Hello World

Enter fullscreen mode Exit fullscreen mode

Use a simple name:

sentinel-cf

Enter fullscreen mode Exit fullscreen mode

Deploy the starter Worker first. This proves the Worker route exists before adding the full security console code.

3. Protect the Worker with Cloudflare Access

Before exposing the security console, put Cloudflare Access in front of it.

In the Worker creation flow or the Worker Access tab:

Protect with Cloudflare Access: On
Scope: All traffic
Policy action: Allow
Policy name: sentinel-cf-bootstrap-admin

Enter fullscreen mode Exit fullscreen mode

For a single UAT administrator, use your own verified identity. For a team, use an Access group or identity provider group instead of adding users one by one.

A production policy should usually require:

  • Corporate identity provider login.
  • MFA.
  • Named security group membership.
  • Short session duration, such as 6 or 12 hours.

4. Add Workers AI binding

Open the Worker:

Workers & Pages -> sentinel-cf -> Bindings -> Add binding -> Workers AI

Enter fullscreen mode Exit fullscreen mode

Set:

Variable name: AI

Enter fullscreen mode Exit fullscreen mode

Workers AI bindings allow Worker code to invoke models through env.AI.run(...). See Workers AI bindings.

5. Create and bind KV

Create a KV namespace for the latest scheduled digest:

Workers & Pages -> KV -> Create namespace

Enter fullscreen mode Exit fullscreen mode

Use:

sentinel-cf-uat-digests

Enter fullscreen mode Exit fullscreen mode

Then bind it to the Worker:

Workers & Pages -> sentinel-cf -> Bindings -> Add binding -> KV namespace

Enter fullscreen mode Exit fullscreen mode

Set:

Variable name: DIGEST_KV
KV namespace: sentinel-cf-uat-digests

Enter fullscreen mode Exit fullscreen mode

KV is used only for storing the latest generated digest. It is not used for secrets.

6. Configure AI Gateway

Create an AI Gateway for observability and control:

AI -> AI Gateway -> Create gateway

Enter fullscreen mode Exit fullscreen mode

Use:

Gateway ID: sentinel-cf-gateway
Collect logs: On, if approved by your security policy
Cache responses: Off for security analytics
Rate limit requests: On for production
Spend limits: On for production
Authenticated Gateway: On where available

Enter fullscreen mode Exit fullscreen mode

Security analytics can contain sensitive paths, rule names, source geography, and investigation context. Treat AI Gateway logs as security logs and restrict who can read them.

7. Add runtime variables and secrets

Open:

Workers & Pages -> sentinel-cf -> Settings -> Variables and Secrets

Enter fullscreen mode Exit fullscreen mode

Add this secret:

Type: Secret
Name: CF_ANALYTICS_TOKEN
Value: <the read-only analytics token>

Enter fullscreen mode Exit fullscreen mode

Add these plain variables:

APP_HOSTNAME=sentinel-cf.example.workers.dev
AI_GATEWAY_ID=sentinel-cf-gateway
ALLOWED_ZONE_TAGS=<cloudflare-zone-id>
ENVIRONMENT=uat
DEFAULT_DIGEST_RANGE=7d
DIGEST_TITLE=SENTINEL-CF Security Posture Digest

Enter fullscreen mode Exit fullscreen mode

ALLOWED_ZONE_TAGS must be Cloudflare Zone IDs, not domain names. If you allow more than one zone, use a comma-separated list:

ALLOWED_ZONE_TAGS=<zone-id-1>,<zone-id-2>

Enter fullscreen mode Exit fullscreen mode

8. Deploy the Worker code

Open:

Workers & Pages -> sentinel-cf -> Edit code

Enter fullscreen mode Exit fullscreen mode

Replace the starter Worker code with the sentinel-cf-worker.js implementation and deploy it.

The deployed Worker should render HTML by default. JSON should be available only where intentionally exposed, such as /healthz or the Show raw JSON section in query results.

9. Validate the health endpoint

Open:

https://sentinel-cf.example.workers.dev/healthz

Enter fullscreen mode Exit fullscreen mode

Expected:

{
  "status": "ok",
  "mode": "read_only",
  "features": ["nlq", "allowed_query_catalog", "digest", "ray_id_investigator"]
}

Enter fullscreen mode Exit fullscreen mode

10. Test the approved query catalogue

Open:

https://sentinel-cf.example.workers.dev/allowed-queries

Enter fullscreen mode Exit fullscreen mode

The page should show approved analyst questions with:

  • Category.
  • Allowed query.
  • Default window.
  • Expected output.
  • Copy.
  • Use.

Use opens /query with the selected question and default window pre-filled. The Worker still enforces fixed read-only intents and zone allowlisting.

11. Test the natural language query UI

Open:

https://sentinel-cf.example.workers.dev/query

Enter fullscreen mode Exit fullscreen mode

Try approved defensive questions such as:

Show me top source countries today
Show potential SQL injection events
Show blocked WAF events in the last 24 hours
Show top targeted URLs this week
Show noisy WAF rules in the last 7 days

Enter fullscreen mode Exit fullscreen mode

The Security NLQ page should return readable cards and tables, not raw JSON by default. Raw JSON remains available behind Show raw JSON for validation.

12. Test the digest

Open:

https://sentinel-cf.example.workers.dev/digest?range=7d

Enter fullscreen mode Exit fullscreen mode

You should see:

  • Risk rating.
  • Total returned security events.
  • Blocked or challenged count.
  • Top actions.
  • Top countries.
  • Top targeted paths.
  • Noisy rules with rule name and rule ID where available.
  • AI executive summary.

13. Test Ray ID investigation

Open:

https://sentinel-cf.example.workers.dev/ray

Enter fullscreen mode Exit fullscreen mode

Enter a Ray ID from Cloudflare Security Events or response headers.

Cloudflare Ray IDs are useful for correlating a request across Security Events, Log Explorer, and server logs, but Cloudflare notes they are not guaranteed unique in all situations. See Cloudflare Ray ID.

14. Add the Cron Trigger

After manual digest testing works, add a weekly Cron Trigger:

Workers & Pages -> sentinel-cf -> Settings -> Trigger events -> Cron triggers -> Add

Enter fullscreen mode Exit fullscreen mode

Use a weekly UTC schedule:

0 1 * * 1

Enter fullscreen mode Exit fullscreen mode

Cloudflare Cron Triggers run on UTC time and can take several minutes to propagate. See Cron Triggers.

When the cron runs, the Worker should generate the digest and store the latest copy in:

DIGEST_KV

Enter fullscreen mode Exit fullscreen mode

Open:

https://sentinel-cf.example.workers.dev/digest/latest

Enter fullscreen mode Exit fullscreen mode

to confirm the stored digest renders.

Terraform implementation second

After the console deployment is working, use Terraform to make the setup repeatable for UAT and production. The Terraform should represent the proven console configuration rather than introducing a separate design.

A clean Terraform handoff should include:

main.tf
variables.tf
outputs.tf
terraform.tfvars.example
sentinel-cf-worker.js
cron-trigger.tf.example
README.md

Enter fullscreen mode Exit fullscreen mode

Keep the Worker JavaScript in sentinel-cf-worker.js and reference it from Terraform. This keeps the code reviewable and avoids burying a large Worker body inside Terraform.

The important resources are:

resource "cloudflare_worker" "sentinel_cf" {
  account_id = var.cloudflare_account_id
  name       = "sentinel-cf"
}

resource "cloudflare_workers_kv_namespace" "sentinel_cf_digests" {
  account_id = var.cloudflare_account_id
  title      = "sentinel-cf-${var.environment}-digests"

  lifecycle {
    prevent_destroy = true
  }
}

Enter fullscreen mode Exit fullscreen mode

The Worker version should bind Workers AI, KV, plain variables, and the read-only secret:

bindings = [
  {
    type = "ai"
    name = "AI"
  },
  {
    type         = "kv_namespace"
    name         = "DIGEST_KV"
    namespace_id = cloudflare_workers_kv_namespace.sentinel_cf_digests.id
  },
  {
    type = "plain_text"
    name = "APP_HOSTNAME"
    text = var.app_hostname
  },
  {
    type = "plain_text"
    name = "AI_GATEWAY_ID"
    text = var.ai_gateway_id
  },
  {
    type = "plain_text"
    name = "ALLOWED_ZONE_TAGS"
    text = join(",", var.allowed_zone_tags)
  },
  {
    type = "plain_text"
    name = "ENVIRONMENT"
    text = var.environment
  },
  {
    type = "plain_text"
    name = "DEFAULT_DIGEST_RANGE"
    text = var.default_digest_range
  },
  {
    type = "plain_text"
    name = "DIGEST_TITLE"
    text = var.digest_title
  },
  {
    type = "secret_text"
    name = "CF_ANALYTICS_TOKEN"
    text = var.cf_analytics_token
  }
]

Enter fullscreen mode Exit fullscreen mode

Expose the important URLs as outputs:

output "worker_url" {
  value = "https://${var.app_hostname}"
}

output "allowed_queries_url" {
  value = "https://${var.app_hostname}/allowed-queries"
}

output "digest_url" {
  value = "https://${var.app_hostname}/digest?range=${var.default_digest_range}"
}

output "ray_investigator_url" {
  value = "https://${var.app_hostname}/ray"
}

Enter fullscreen mode Exit fullscreen mode

Run:

terraform init
terraform fmt -recursive
terraform validate
terraform plan

Enter fullscreen mode Exit fullscreen mode

If the Worker or Access app already exists from the dashboard, import existing resources before apply. Do not let Terraform destroy and recreate Access controls without explicit approval.

The recommended production migration is:

  • Build and validate in the console for UAT.
  • Put the final Worker code and Terraform files in version control.
  • Import existing Cloudflare resources where needed.
  • Run terraform plan and review the proposed changes.
  • Apply only after Access, token scope, KV, AI binding, and route ownership are understood.

Operational validation

Test Expected result /healthz Worker healthy and read-only. /allowed-queries Approved query list renders; Copy and Use actions work. /query HTML cards/tables render; events show Bangkok-local time and selected window labels. /digest?range=7d HTML digest renders; noisy rules include rule name and rule ID when available. /digest/latest Latest scheduled digest renders from KV after cron has run. /ray Ray investigation form loads. Invalid zone Request is rejected. Write-style prompt No change is performed. Unauthenticated request Blocked by Cloudflare Access.

Production hardening

Before production:

  • Use separate production token.
  • Use separate production Access policy.
  • Use separate KV namespace.
  • Keep Terraform state encrypted and access-controlled.
  • Restrict AI Gateway logs to approved admins.
  • Confirm Worker logs do not expose raw questions or secrets.
  • Validate against retained Logpush or SIEM records.

Final position

This is a useful pattern because it gives analysts a faster way to understand Cloudflare security activity without handing automation unsafe authority.

The Worker is the guardrail. AI is the analyst assistant. Terraform is the control plane. That division is what makes the design operationally credible.

원문에서 계속 ↗