Skip to main content

Cloudflare Setup for dfe-fetcher

What a Cloudflare administrator needs to configure so dfe-fetcher can read account-level audit logs.

Overview​

dfe-fetcher pulls account audit-log entries from the v1 REST endpoint https://api.cloudflare.com/client/v4/accounts/{account_id}/audit_logs, one Cloudflare account per fetcher instance. It requests a time window (since / before RFC 3339) with per_page, optional actor.email and action.type filters, and pages by number using result_info.total_pages from Cloudflare's {"success":..., "result":[...], "result_info":{...}} envelope. Authentication is a scoped API token in Authorization: Bearer <token>.

The source is the shipped cloudflare profile (crates/fetcher/profiles/cloudflare.yaml); the sources.cloudflare block below maps onto an instance of it at load. A 429 or 5xx is retried with backoff (honouring Retry-After), a 401 or 403 ends the tick, a 2xx carrying success: false fails the tick with the errors array as the reason, and a tick that fails does not advance the fetch window. A missing token or account_id is refused at load, naming sources.cloudflare.

Note on API versions: Cloudflare made Audit Logs v2 (the newer /accounts/{account_id}/logs/audit endpoint) generally available in early 2026. dfe-fetcher targets the v1 audit_logs endpoint, which is still live - Cloudflare has not published a firm retirement date for v1. If/when v1 is sunset, this source will need to move to v2; track the references below.

Prerequisites​

  • Any Cloudflare account. Account audit logs are available on all plans; no paid add-on is required to read them via the API.
  • An account role that can create API tokens and read account settings (Super Administrator, or Administrator). User API tokens are created from your user profile, scoped to the chosen account.
  • Your 32-character hex Account ID (see below).

Required Permissions​

ServiceEndpointPermission or ScopeNotes
audit_logsGET /accounts/{account_id}/audit_logs (v1)Account Settings: Read (Account -> Account Settings -> Read)This is the permission that actually works for the v1 audit_logs endpoint. There is NO permission literally named "Account Audit Logs Read" for v1.
audit_logs (v2, future)GET /accounts/{account_id}/logs/auditAccount Audit Logs Read (a.k.a. "Get account audit logs (Version 2)") or Account Settings Read/WriteOnly relevant once dfe-fetcher moves to the v2 endpoint.

Scope the token to the single target account (not "all accounts") and keep it read-only.

Source-Side Setup​

1. Find your Account ID​

In the Cloudflare dashboard (dash.cloudflare.com), open the account, then either:

  • Click the menu button (three dots) next to the account name on the Account Home page and choose Copy account ID; or
  • On the account Overview page, scroll to the API section at the bottom and copy the Account ID.

It is a 32-character hex string. This is the account_id config field.

2. Create a scoped API token​

  1. Top-right profile -> My Profile -> API Tokens -> Create Token.

  2. Scroll to Create Custom Token -> Get started.

  3. Name it dfe-fetcher-audit.

  4. Under Permissions, select Account / Account Settings / Read.

  5. Under Account Resources, select Include -> (do not grant all accounts).

  6. (Recommended) Under Client IP Address Filtering, restrict to the fetcher's egress IP, and set a TTL to bound the token's lifetime.

  7. Continue to summary -> Create Token. Copy the token now - it is shown once. New tokens use the scannable cfut_-prefixed format.

    # Smoke-test the v1 endpoint:
    curl -sS \
    "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/audit_logs?per_page=1" \
    -H "Authorization: Bearer $CLOUDFLARE_TOKEN"
    # Verify the token itself:
    curl -sS "https://api.cloudflare.com/client/v4/user/tokens/verify" \
    -H "Authorization: Bearer $CLOUDFLARE_TOKEN"

dfe-fetcher Configuration​

Config File​

sources:
cloudflare:
enabled: true
account_id: "0123456789abcdef0123456789abcdef" # 32-char hex
token: "cfut_your_api_token_here"
services:
- name: audit_logs
config:
actor_email: "ops@example.com" # optional, filter by acting user
action_type: "login" # optional, filter by action category
per_page: 100 # default 100, max 1000
topic: "cloudflare"
# filter: 'action.type != "view"' # CEL, hot-reloaded

Environment Variables​

Pattern is DFE_FETCHER_SOURCES__CLOUDFLARE__<FIELD> (double underscores).

DFE_FETCHER_SOURCES__CLOUDFLARE__ENABLED="true"
DFE_FETCHER_SOURCES__CLOUDFLARE__ACCOUNT_ID="0123456789abcdef0123456789abcdef"
DFE_FETCHER_SOURCES__CLOUDFLARE__TOKEN="cfut_your_api_token_here"

Secrets Manager​

Keep the token out of the config file with credential_secret, which resolves to the literal token value:

sources:
cloudflare:
enabled: true
account_id: "0123456789abcdef0123456789abcdef"
credential_secret: "vault:kv/data/cloudflare:token"
services:
- name: audit_logs
topic: "cloudflare"

Verification​

  • Health check. The profile's probe calls GET /user/tokens/verify; a 2xx confirms the token is valid and active. Note this proves the token is good but not that it carries Account Settings Read - exercise the smoke test for that.

  • e2e smoke test. crates/fetcher/tests/e2e/smoke_remote.rs has #[ignore]-gated live tests. Export credentials (or put them in .env-cloud), then run:

    export CLOUDFLARE_ACCOUNT_ID="0123456789abcdef0123456789abcdef"
    export CLOUDFLARE_TOKEN="cfut_..."
    cargo test -p dfe-fetcher --test e2e cloudflare_ -- --ignored
  • Common failure modes.

    • 403 Forbidden / success:false with an authentication error: token lacks Account Settings: Read, or is scoped to the wrong account. Adding Account Settings Read is the usual fix - do NOT go hunting for an "Audit Logs" permission for the v1 endpoint.
    • 400 / 404: malformed or wrong account_id (must be the 32-char hex account ID, not a zone ID).
    • Body success:false on a 2xx: the tick fails with the errors array as the reason and the window does not advance.
    • Empty result every tick: no audit events in the configured lookback window.

Cost​

dfe-fetcher only reads. Reading account audit logs through the API is not expected to carry an additional charge, but this can depend on your Cloudflare plan. Confirm any cost implications against your own Cloudflare plan.

References​