Cloudflare provider for octorules
Project description
octorules-cloudflare
Cloudflare provider for octorules — manages 23 Cloudflare rule phases, custom rulesets, lists, and Page Shield policies as YAML.
Installation
pip install octorules-cloudflare
Or via the octorules extra:
pip install octorules[cloudflare]
This installs octorules (core), octorules-cloudflare, and octorules-wirefilter (Rust FFI bridge to Cloudflare's wirefilter engine for authoritative expression parsing and full linter coverage).
Prebuilt wirefilter wheels are available for Linux (x86_64, aarch64; glibc and musl/Alpine), macOS (x86_64, ARM64), and Windows (x86_64).
Configuration
providers:
cloudflare:
token: env/CLOUDFLARE_API_TOKEN
rules:
directory: ./rules
zones:
example.com:
sources:
- rules
The env/ prefix resolves values from environment variables at runtime.
All keys under the provider section are forwarded to the provider constructor
as keyword arguments (octodns-style passthrough).
Authentication
A Cloudflare API token is required. The token needs the following permissions:
- Zone > Firewall Services > Edit — for ruleset phase operations
- Account > Account Rulesets > Edit — for account-level rules and custom rulesets
- Account > Account Filter Lists > Edit — for list management
- Zone > Page Shield > Edit — for Page Shield policy management
See docs/permissions.md for a detailed mapping of API token permissions to octorules features.
Provider settings
All settings below go under the provider section (e.g. providers.cloudflare).
| Key | Default | Description |
|---|---|---|
token |
(required) | Cloudflare API token (env/ prefix supported) |
max_retries |
2 |
API retry count (0-10) |
timeout |
30 |
API timeout in seconds (max 300) |
Safety thresholds are configured under safety: (framework-owned, not
forwarded to the provider):
| Key | Default | Description |
|---|---|---|
safety.delete_threshold |
30.0 |
Max % of rules that can be deleted |
safety.update_threshold |
30.0 |
Max % of rules that can be updated |
safety.min_existing |
3 |
Min rules before thresholds apply |
Supported features
| Feature | Status |
|---|---|
| Phase rules (23 phases) | Supported |
| Custom rulesets (account-level) | Supported |
| Lists (IP, ASN, hostname, redirect) | Supported |
| Page Shield policies (zone-level) | Supported |
Zone discovery (list_zones) |
Supported |
| Account-level scopes | Supported |
Audit IP extraction (octorules audit) |
Supported |
Bot Management settings (cloudflare_bot_management) |
Supported |
URL normalization settings (cloudflare_url_normalization) |
Supported |
Zone security defaults (cloudflare_zone_security) |
Supported |
Leaked Credential Check (cloudflare_leaked_credential_check) |
Supported |
Content Scanning / anti-malware (cloudflare_content_scanning) |
Supported |
Supported phases
23 Cloudflare phases — 18 HTTP request/response phases and 5 network-level (Magic Transit) phases. Phases execute in a fixed order:
Request -> url_normalization -> redirect_rules -> url_rewrite_rules -> request_header_rules
-> origin_rules -> config_rules -> cache_rules
-> waf_custom_rules -> waf_managed_rules -> rate_limiting_rules
-> bot_fight_rules -> http_ddos_rules
-> Origin fetch <-
-> custom_error_rules -> response_header_rules -> compression_rules
-> sensitive_data_detection -> log_custom_fields -> Response
Phases with a default action (e.g., redirect_rules -> redirect) don't need action in the YAML — it's injected automatically. For phases without a default (e.g., waf_custom_rules), you must specify action explicitly.
Phases marked as both Zone and Account work at either scope. Account-only phases are skipped for zone scopes and vice versa, eliminating wasted API calls.
For the full phase reference — execution order diagram, valid actions per phase, field/function availability, and key behaviors — see docs/lint/README.md.
Note:
waf_managed_exceptionswas renamed towaf_managed_rules. The old name still works as an alias but is deprecated — update your YAML files to use the new name.
Expression syntax
Rule expressions use Cloudflare's ruleset expression language. When octorules-wirefilter is installed (included automatically with octorules-cloudflare), expressions are parsed by Cloudflare's actual wirefilter engine, providing authoritative type checking, field validation, and syntax verification. Without it, a regex-based fallback parser extracts fields, functions, operators, and literals but cannot perform type checking.
The linter logs which parser is active at startup (Expression parser: wirefilter or Expression parser: regex fallback).
Rule-level metadata: All Cloudflare rules support the
octorules:key for per-rule metadata —ignored: trueto skip a rule during plan/sync, andincluded/excludedto restrict rules to specific providers. See octorules core docs for syntax and examples.
Custom rulesets (account-level)
At the account level, WAF custom rules and rate limiting rules use a two-tier structure: the phase entrypoint contains deploy rules (action: execute) that reference child custom rulesets by ID. The individual blocking/logging rules live inside those child rulesets.
octorules manages both tiers. Deploy rules are managed via the normal phase sections (waf_custom_rules, rate_limiting_rules). The individual rules inside each custom ruleset are managed via a separate custom_rulesets section:
# Account rules file (e.g. rules/my-account.yaml)
# Deploy rules (phase entrypoint — references child rulesets by ID)
waf_custom_rules:
- ref: deploy-known-attackers
description: Deploy known attackers ruleset
action: execute
action_parameters:
id: abc12345def67890abc12345def67890
version: latest
enabled: true
expression: (http.host eq "api.example.com")
# Individual rules inside each custom ruleset
custom_rulesets:
- id: abc12345def67890abc12345def67890
name: Known attackers
phase: http_request_firewall_custom
rules:
- ref: block-bad-asn
description: Block by AS number
action: block
expression: (ip.geoip.asnum in {12345 67890})
- ref: block-bad-ua
description: Block by user-agent
action: block
expression: (http.user_agent contains "BadBot")
The id field in each custom_rulesets entry links it to the deploy rule's action_parameters.id. Rules inside use ref for identification (same pattern as phase rules). Every rule must specify an action explicitly.
Use octorules dump --scope account to export existing custom rulesets to YAML.
Note: octorules manages rules within existing custom rulesets. Creating or deleting rulesets themselves must be done via the Cloudflare dashboard. Zone-level rulesets do not have
kind=customchildren — this is account-level only.
Lists (account-level)
Cloudflare account-level Lists (IP lists, ASN lists, hostname lists, redirect lists) can be referenced in rule expressions via $list_name syntax. octorules manages full lifecycle of lists declaratively: create, delete, update metadata, and manage items.
Add a top-level lists key to your account rules file:
# rules/my-account.yaml
lists:
- name: blocked_ips
kind: ip
description: "Known bad IPs"
items:
- ip: "1.2.3.4"
comment: "Scanner"
- ip: "5.6.7.0/24"
comment: "Botnet range"
- name: partner_asns
kind: asn
description: "Partner AS numbers"
items:
- asn: 12345
comment: "Partner A"
- asn: 67890
comment: "Partner B"
Each list entry requires:
| Field | Description |
|---|---|
name |
List name — matches CF list name and $list_name in expressions |
kind |
One of ip, asn, hostname, redirect |
description |
Optional — updated if changed |
items |
List of items (can be empty [] to clear all items) |
How it works:
- The presence of a
lists:key means ALL lists are managed — lists in Cloudflare not in YAML are planned for deletion (subject to safety thresholds). - If the
lists:key is absent, lists are ignored entirely. - Item updates are asynchronous — octorules polls the bulk operation until completion.
- During sync, lists are applied before rulesets and phases, so newly created lists are available for rule expressions that reference them.
- Use
octorules dump --scope accountto export existing lists to YAML. The dump externalizes list items into separate files (referenced via!includetags) underproviders.lists.directory(default:{rules_dir}/custom_lists).
Reference lists in rule expressions:
waf_custom_rules:
- ref: block-bad-ips
description: Block IPs from blocklist
action: block
expression: (ip.src in $blocked_ips)
Page Shield policies (zone-level)
Cloudflare Page Shield manages Content Security Policies (CSP) at the zone level. octorules manages full lifecycle of Page Shield policies declaratively: create, update, and delete.
Add a top-level page_shield_policies key to your zone rules file:
# rules/example.com.yaml
page_shield_policies:
- description: "CSP on all example.com"
action: allow
expression: "true"
enabled: true
value: >-
script-src 'self' 'unsafe-inline' 'unsafe-eval' https:;
worker-src 'self' blob:
- description: "Log CSP on staging"
action: log
expression: '(http.host eq "staging.example.com")'
enabled: true
value: "default-src 'self'"
Each policy entry requires:
| Field | Description |
|---|---|
description |
Policy description — used as the identity key for matching |
action |
allow or log |
expression |
Cloudflare filter expression |
enabled |
Boolean |
value |
CSP directive string |
How it works:
- The
descriptionfield is the identity key (likereffor rules andnamefor lists). Policies are matched between YAML and Cloudflare by description. - The presence of a
page_shield_policies:key means ALL policies are managed — policies in Cloudflare not in YAML are planned for deletion. - If the
page_shield_policies:key is absent, policies are ignored entirely. - During sync, policies are applied after lists and before custom rulesets and phases.
- Use
octorules dumpto export existing Page Shield policies to YAML.
CSP source normalization: The order of sources within a CSP directive is not significant — script-src 'self' example.com and script-src example.com 'self' are semantically identical. octorules normalizes source order (sorted alphabetically within each directive) before comparing value fields, so reordering sources in your YAML will not trigger an upstream change on Cloudflare. You can freely reorganize sources for readability without causing a sync.
Linting
140 Cloudflare-specific lint rules (CF prefix) across 6 ranges:
| Range | Category | Rules |
|---|---|---|
| CF001–CF026 | Structure & parse | 26 |
| CF100–CF104 | Cross-rule ordering | 5 |
| CF200–CF222 | Action validation | 23 |
| CF300–CF309 | Expression, function & type | 10 |
| CF400–CF476 | Domain-specific (rate limit, cache, config, redirect, transform, origin, page shield, list) | 41 |
| CF500–CF545 | Plan limits, style & value constraints | 35 |
See docs/lint/README.md for the full rule reference.
Development
git clone git@github.com:doctena-org/octorules-cloudflare.git
cd octorules-cloudflare
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
ln -sf ../../scripts/hooks/pre-commit .git/hooks/pre-commit
The pre-commit hook auto-regenerates schemas.json (the frozen schema fallback
for users without wirefilter) whenever overlay.toml or pyproject.toml
changes. See docs/schemas.md for the full schema
architecture.
License
Apache License 2.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file octorules_cloudflare-0.7.1.tar.gz.
File metadata
- Download URL: octorules_cloudflare-0.7.1.tar.gz
- Upload date:
- Size: 171.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d4943e49f61121537bcf67076a058d2361e9cf5d75f07d091261ce83148e654
|
|
| MD5 |
f72aebe97b72f09caeaa24f1617bc7ab
|
|
| BLAKE2b-256 |
f0f5d9b99017e6437a76ddae8b5f28bc193f91b57a78e83f06252d94524fd575
|
Provenance
The following attestation bundles were made for octorules_cloudflare-0.7.1.tar.gz:
Publisher:
release.yaml on doctena-org/octorules-cloudflare
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
octorules_cloudflare-0.7.1.tar.gz -
Subject digest:
5d4943e49f61121537bcf67076a058d2361e9cf5d75f07d091261ce83148e654 - Sigstore transparency entry: 1242723124
- Sigstore integration time:
-
Permalink:
doctena-org/octorules-cloudflare@be393565634c451cd85d9f959440587177efd018 -
Branch / Tag:
refs/tags/v0.7.1 - Owner: https://github.com/doctena-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@be393565634c451cd85d9f959440587177efd018 -
Trigger Event:
push
-
Statement type:
File details
Details for the file octorules_cloudflare-0.7.1-py3-none-any.whl.
File metadata
- Download URL: octorules_cloudflare-0.7.1-py3-none-any.whl
- Upload date:
- Size: 203.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92c655cf9daa6945cbe5a1692e485193b03ee43ec6296816738f9c9975702171
|
|
| MD5 |
e6b9ebbfab82cc6dc7151c20e979c3ec
|
|
| BLAKE2b-256 |
96d2679b791d6b803d33a9d6c3bc7f12e47157a858494a8736a553525ef5be15
|
Provenance
The following attestation bundles were made for octorules_cloudflare-0.7.1-py3-none-any.whl:
Publisher:
release.yaml on doctena-org/octorules-cloudflare
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
octorules_cloudflare-0.7.1-py3-none-any.whl -
Subject digest:
92c655cf9daa6945cbe5a1692e485193b03ee43ec6296816738f9c9975702171 - Sigstore transparency entry: 1242723143
- Sigstore integration time:
-
Permalink:
doctena-org/octorules-cloudflare@be393565634c451cd85d9f959440587177efd018 -
Branch / Tag:
refs/tags/v0.7.1 - Owner: https://github.com/doctena-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@be393565634c451cd85d9f959440587177efd018 -
Trigger Event:
push
-
Statement type: