Azure WAF provider for octorules
Project description
octorules-azure
Azure WAF provider for octorules — manages Azure Web Application Firewall custom rules as YAML.
Supports both Azure Front Door WAF (Premium/Standard) and Application Gateway WAF (WAF_v2) through a unified interface. Users write the same YAML regardless of which WAF type is deployed.
Alpha — this provider has comprehensive offline test coverage (649 tests) but has not yet been tested against live Azure WAF APIs. Use with caution and report any issues.
Installation
pip install octorules-azure
This installs octorules (core) and octorules-azure. The provider is auto-discovered — no class: needed in config.
Configuration
# config.yaml
providers:
azure:
subscription_id: env/AZURE_SUBSCRIPTION_ID
resource_group: rg-waf-production
waf_type: front_door # or "app_gateway"
timeout: 30 # API timeout in seconds
max_workers: 1 # parallel workers for multi-zone ops
rules:
directory: ./rules
zones:
api-gateway-waf-policy:
sources:
- rules
corporate-site-waf-policy:
sources:
- rules
Each zone name maps to an Azure WAF policy name. The provider resolves policy names at runtime. 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).
Provider settings
All settings below go under the provider section (e.g. providers.azure).
| Key | Default | Description |
|---|---|---|
subscription_id |
AZURE_SUBSCRIPTION_ID env var |
Azure subscription ID (required) |
resource_group |
AZURE_RESOURCE_GROUP env var |
Resource group containing WAF policies (required) |
waf_type |
front_door / AZURE_WAF_TYPE env var |
"front_door" or "app_gateway" |
timeout |
30 |
API timeout in seconds |
max_workers |
1 |
Parallel workers for multi-zone operations |
Authentication
Uses DefaultAzureCredential — no token is needed in the config file. Common options:
- Service principal (CI/CD):
AZURE_TENANT_ID+AZURE_CLIENT_ID+AZURE_CLIENT_SECRETenv vars - Managed identity (Azure VMs, ACI, App Service): assign identity to the resource, grant WAF Contributor role
- Azure CLI (local development): run
az loginbefore octorules commands - Certificate (automated systems):
AZURE_TENANT_ID+AZURE_CLIENT_ID+AZURE_CLIENT_CERTIFICATE_PATHenv vars
The service principal or managed identity needs the Contributor or Network Contributor role on the resource group containing WAF policies.
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 | Azure concept |
|---|---|---|
| Phase rules (3 phases) | Supported | Custom rules, rate limit rules, managed rules |
| Policy settings | Supported | WAF mode, request body inspection, file upload limits |
| Custom rulesets | Not supported | Azure has no separate rule group concept |
| Lists (IP Sets) | Not supported | IPs are inline in matchConditions (up to 600 per condition) |
Zone discovery (list_zones) |
Supported | Lists WAF policies in resource group |
| ETag concurrency control | Supported | Retries on HTTP 412 (concurrent update conflict) |
Phases
| Phase | Rule Type | Description |
|---|---|---|
azure_waf_custom_rules |
MatchRule |
Standard WAF rules (IP blocks, geo-blocks, header checks, etc.) |
azure_waf_rate_rules |
RateLimitRule |
Rate limiting rules with threshold, duration, and grouping |
azure_waf_managed_rules |
Managed | OWASP DRS, Bot Protection rule sets with overrides and exclusions |
Rule format
The same YAML format works for both Front Door and Application Gateway. The adapter translates API-level differences (field naming, SDK models) transparently.
Custom rules
# rules/api-gateway-waf-policy.yaml
azure_waf_custom_rules:
# Block specific IP ranges
- ref: BlockBadIPs
priority: 1
action: Block
matchConditions:
- matchVariable: RemoteAddr
operator: IPMatch
matchValue:
- 192.168.1.0/24
- 10.0.0.0/8
# Block requests with suspicious user-agent
- ref: BlockBadUserAgents
priority: 2
action: Block
matchConditions:
- matchVariable: RequestHeader
selector: User-Agent
operator: Contains
matchValue:
- evilbot
- scanner
transforms:
- Lowercase
# Geo-block: allow only US and CA
- ref: GeoBlock
priority: 3
action: Block
matchConditions:
- matchVariable: RemoteAddr
operator: GeoMatch
negateCondition: true
matchValue:
- US
- CA
# Block requests matching a regex pattern
- ref: BlockAdminPaths
priority: 4
action: Block
matchConditions:
- matchVariable: RequestUri
operator: RegEx
matchValue:
- "^/admin/.*\\.php$"
transforms:
- Lowercase
Rate-limit rules
azure_waf_rate_rules:
# Rate limit API endpoints to 100 requests per minute per client IP
- ref: RateLimitAPI
priority: 100
action: Block
ruleType: RateLimitRule
rateLimitDurationInMinutes: 1
rateLimitThreshold: 100
groupBy:
- variableName: SocketAddr
matchConditions:
- matchVariable: RequestUri
operator: BeginsWith
matchValue:
- /api/
Rule fields reference
| Field | Type | Required | Description |
|---|---|---|---|
ref |
string | Yes | Rule name (letters, digits, underscores; max 128 chars) |
priority |
int | Yes | Evaluation order (positive int, lower = first; must be unique across all rules) |
action |
string | Yes | Allow, Block, Log, Redirect, AnomalyScoring, or JSChallenge |
enabledState |
string | No | Enabled (default) or Disabled |
ruleType |
string | No | MatchRule (default) or RateLimitRule |
matchConditions |
list | Yes | One or more conditions (ANDed); max 10 per rule |
rateLimitDurationInMinutes |
int | RateLimitRule | 1 or 5 (time window) |
rateLimitThreshold |
int | RateLimitRule | Requests per client in the window (10 — 1,000,000) |
groupBy |
list | No | Rate limit grouping: SocketAddr, GeoLocation, or None |
Match condition fields
| Field | Type | Description |
|---|---|---|
matchVariable |
string | What to match: RemoteAddr, SocketAddr, RequestMethod, QueryString, PostArgs, RequestUri, RequestHeader, RequestBody, Cookies |
selector |
string | Header/cookie/post-arg name (required for RequestHeader, Cookies, PostArgs) |
operator |
string | Any, IPMatch, GeoMatch, Equal, Contains, BeginsWith, EndsWith, RegEx, LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual, ServiceTagMatch |
negateCondition |
bool | Invert the match (default: false) |
matchValue |
list | Values to match against (max 600 for IPMatch, 10 for string operators) |
transforms |
list | Pre-processing: Lowercase, Uppercase, Trim, UrlDecode, UrlEncode, RemoveNulls, HtmlEntityDecode |
WAF Type Differences
The adapter pattern handles all API differences transparently. Users write identical YAML regardless of waf_type:
| Feature | Front Door | Application Gateway |
|---|---|---|
| Scope | Global (edge CDN) | Regional (reverse proxy) |
| Propagation | Up to 45 minutes | Seconds to minutes |
| SDK | azure-mgmt-frontdoor |
azure-mgmt-network |
| Update model | Async LRO (begin_create_or_update) |
Synchronous (create_or_update) |
| Extra actions | Redirect, AnomalyScoring | -- |
| Extra transforms | -- | HtmlEntityDecode |
| Extra operators | ServiceTagMatch | -- |
| Match variable names | RequestHeader, Cookies |
RequestHeaders, RequestCookies (adapter maps) |
| Negation field | negateCondition |
negationConditon (API typo; adapter maps) |
Linting
73 lint rules with AZ prefix covering structure, priority, action, match conditions, rate limits, cross-rule analysis, best practices, and managed rule sets. See docs/lint.md for the full reference with examples.
octorules lint --config config.yaml
Known limitations
- Front Door propagation delay: Configuration changes can take up to 45 minutes to propagate to all edge nodes globally. Plan accordingly for production deployments.
- No PATCH API: Azure WAF uses full-policy PUT. octorules always fetches the current policy first and merges changes, but concurrent external modifications during sync could conflict (mitigated by ETag retry).
- No custom rulesets or lists: Azure WAF has no equivalent of AWS Rule Groups or IP Sets. IP addresses are inline in
matchConditions(max 600 per condition, 60,000 across all rules). - Regex limit: Maximum 5 custom rules using the
RegExoperator per policy. - Priority must be globally unique: Rule priorities must be unique across ALL custom rules in a policy (both MatchRule and RateLimitRule combined), not just within a phase.
Development
python -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest tests/ -v
.venv/bin/ruff check octorules_azure/ tests/
.venv/bin/ruff format --check octorules_azure/ tests/
Pre-commit hook:
ln -sf ../../scripts/hooks/pre-commit .git/hooks/pre-commit
License
octorules-azure is licensed under the Apache License 2.0.
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_azure-0.1.7.tar.gz.
File metadata
- Download URL: octorules_azure-0.1.7.tar.gz
- Upload date:
- Size: 76.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d9335da894b97020a9cb409ce19a9a38ce8759f98b1fb2cc79bab70b583b052
|
|
| MD5 |
de0adebc1a91e33406b2ff0b93392e49
|
|
| BLAKE2b-256 |
1a265d860a971c69cc6737710d720ffce75ed95d386b7fbe9156f1797dc2017c
|
Provenance
The following attestation bundles were made for octorules_azure-0.1.7.tar.gz:
Publisher:
release.yaml on doctena-org/octorules-azure
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
octorules_azure-0.1.7.tar.gz -
Subject digest:
8d9335da894b97020a9cb409ce19a9a38ce8759f98b1fb2cc79bab70b583b052 - Sigstore transparency entry: 1283966669
- Sigstore integration time:
-
Permalink:
doctena-org/octorules-azure@164577626da0b849b50274e03fbf818f5050fede -
Branch / Tag:
refs/tags/v0.1.7 - Owner: https://github.com/doctena-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@164577626da0b849b50274e03fbf818f5050fede -
Trigger Event:
push
-
Statement type:
File details
Details for the file octorules_azure-0.1.7-py3-none-any.whl.
File metadata
- Download URL: octorules_azure-0.1.7-py3-none-any.whl
- Upload date:
- Size: 82.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 |
78d4366415e771e018791f468c18fa458aca90a2c7f216c64a16cccd38ce5c79
|
|
| MD5 |
79b80135c9b63323a50e77e621fc110d
|
|
| BLAKE2b-256 |
22e036dd7e3f421a4ba7cb9d8f6e7d04401c424991ad6ae6a1b7907165d790b8
|
Provenance
The following attestation bundles were made for octorules_azure-0.1.7-py3-none-any.whl:
Publisher:
release.yaml on doctena-org/octorules-azure
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
octorules_azure-0.1.7-py3-none-any.whl -
Subject digest:
78d4366415e771e018791f468c18fa458aca90a2c7f216c64a16cccd38ce5c79 - Sigstore transparency entry: 1283966719
- Sigstore integration time:
-
Permalink:
doctena-org/octorules-azure@164577626da0b849b50274e03fbf818f5050fede -
Branch / Tag:
refs/tags/v0.1.7 - Owner: https://github.com/doctena-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@164577626da0b849b50274e03fbf818f5050fede -
Trigger Event:
push
-
Statement type: