Query firewall
Per-zone (or workspace-default) rules that refuse DNS queries at the authoritative server. Useful to block ANY/AXFR amplification, lock an internal zone to corp ranges, or stop a single resolver from flooding you.
A refused query gets DNS REFUSED rcode, same as querying a server
that isn’t authoritative for the zone. Refusals show up in the
Refused-query forensics panel within seconds.
Where it lives
- Workspace default: Policies page → “Tenant defaults” card. Applies to every zone unless that zone has its own firewall config.
- Per-zone override: Policies page → expand the zone → “Query firewall” toggle. A per-zone config replaces the default entirely (not merged).
Config shape
The firewall config is a JSON object. Every field is optional:
{
"allow_sources": ["10.0.0.0/8", "203.0.113.5"],
"deny_sources": ["198.51.100.0/24"],
"allow_countries": ["DE", "FR", "US"],
"deny_countries": ["RU", "KP"],
"refuse_qtypes": ["ANY", "AXFR"],
"rate_limit_qps": 100
}
An empty list means “no rule,” not “deny all.”
Evaluation order
For each query the engine evaluates rules in this order; the first matching rule decides:
- IP deny: source matches
deny_sources. →REFUSED(reason:ip-denied). - IP allow:
allow_sourcesis non-empty and source doesn’t match any entry. →REFUSED(reason:ip-not-allowed). - Country deny: source country (via GeoIP) is in
deny_countries. →REFUSED(country-denied). - Country allow:
allow_countriesnon-empty and source country isn’t in it. →REFUSED(country-not-allowed). - Qtype refuse: query type matches
refuse_qtypes. →REFUSED(qtype-refused). - Rate limit: source IP has exceeded
rate_limit_qpsover the sliding 1-second window. →REFUSED(rate-limited).
Empty lists skip their step. rate_limit_qps: 0 disables rate
limiting (any qps allowed).
ECS handling
When EDNS Client Subnet is present and looks plausible, the engine uses the ECS subnet as the source for CIDR + country rules. ECS makes DNS-level geo less coarse, but still coarse. Don’t depend on this for security-critical decisions; it’s correlation, not identity.
When ECS is absent or implausible (0.0.0.0/0, RFC-1918 from public
resolvers), the engine falls back to the resolver’s source IP.
Common configs
Block amplification basics
{ "refuse_qtypes": ["ANY", "AXFR"] }
The classic “you have a public DNS server” hygiene minimum.
Lock an internal zone to corp ranges
{ "allow_sources": ["10.0.0.0/8", "192.168.0.0/16"] }
Combine with split-horizon by serving the zone only on internal resolvers.
Sanction-list geo block
{ "deny_countries": ["RU", "KP", "IR", "SY"] }
Needs a GeoIP database loaded on the edge, without one, all queries are treated as country=unknown (no match).
Rate-limit a chatty resolver
{ "rate_limit_qps": 200 }
Per source IP, not per zone. A misbehaving Cloudflare resolver gets its own bucket; one bad actor doesn’t burn budget for everyone else behind the same edge.
Inheritance
Each zone resolves to one effective policy:
- A zone with its own enabled firewall config uses that, and only that.
- Otherwise it inherits the workspace default (if enabled).
- Otherwise no firewall runs and nothing is refused.
A per-zone config replaces the default outright, it isn’t merged. So a zone can tighten the default with its own rules, or opt out entirely by enabling an empty config (no rules).
Per-rule audit
Every firewall change lands in Settings → Audit log with who made it, what changed (before and after), and when. Useful for the “who turned this on at 3 AM” question.