What it shows
Pick a span (1 hour to 30 days), a proxy, private or public proxies and a hostname at the top.Events come from the nodes’ request logs, which keep a sample of what they serve. Blocked requests
get ten times the room, so the totals are exact while the event list can miss some. Nodes older
than DNZ 2.5 can’t filter their log for the WAF: the page names them until they are updated.
Private or public proxies
All / Private / Public beside the proxy filter looks at the attacks on local sites and those on public sites apart: which addresses, from where, against what. The category is the proxy’s own access, the one Serve on’s All private and All public use. It can be set together with one proxy (both must hold), it stays in the page’s address (?access=private) so a view can be shared, and the line under the
filters says which proxies are being shown.
A filter that leaves no proxy (private with a public proxy picked, or no proxy marked private) shows nothing and says
why. It never shows everything.
A block or a ban keeps its own scope. It goes where you pick in its dialog (these hostnames, every route with the
WAF, every DNZ Proxy), whatever the page is showing, and the dialog says so. Unblock takes the value out of every
firewall, those of other proxies too.
Look an address up
Click any address on the page.- Place: the country, and the region and city with a city database.
- Network: its ASN and owner, such as
AS14061 DigitalOcean, LLC. Hosting marks the network of a company that rents out servers: requests from there are usually scripts, not people. - Reverse name: the name the address points back to. Confirmed means that name points to the address again; an unconfirmed name proves nothing, since anyone can write any name in their own reverse zone.
- First and last seen, the rules it set off, the hostnames, paths and user agents it used, and its latest events.
- The firewalls already blocking it.
Block an attacker
1
Pick what to block
In an address’s details: Block address, Block its network (the
/24 of an IPv4
address, the /48 of an IPv6 one) or Block its country. A country’s panel and each event
offer the same.2
Pick where
These hostnames (the default) blocks it on the routes serving the hostnames it went for, so
nothing else is touched. Every route with the WAF blocks it on every DNZ Proxy route whose
WAF is on.
3
Block
Its requests get the
403 page before they reach your servers, within seconds on every node.waf-page-addresses holds the addresses and ranges, waf-page-countries the countries. Both go
after the firewall’s own rules, so your allow rules still act first. A route without a firewall gets
the shared waf-blocklist firewall. A firewall other routes use blocks the address there too.
Unblock takes it out of every firewall again.
Bans that expire
A block stays until you take it out. A ban ends by itself: an address the WAF blocked too often is refused for a while, then let back in. It works like fail2ban, but across nodes. Bans are a DNZ Proxy feature on HTTP routes: Traefik and Caddy have no WAF to count, and a TCP or UDP route has no page to answer with. They are off by default. Turn them on per proxy, in its DNZ Proxy settings under Bans that expire:
The node that sees an address cross the number bans it at once, without waiting for DNZ, and the
other nodes have the ban within seconds. Only blocks count: a route in log-only mode bans nobody.
A banned visit gets the
403 Access blocked page with its request ID, never a dropped connection,
so a real visitor banned by mistake sees why and has something to send you. The request log shows
block ban:<rule>, with the rule that led to the ban.
A ban never stops the entry point’s trusted forwarders, addresses the route’s WAF allows, what the
route’s firewall lets through with an allow rule, or DNZ’s own uptime checks.
block ban:decoy. Decoy names
on this page lists who touched what.
Lists of bad networks
Outside lists of bad networks, kept up to date by DNZ. Your nodes never reach the internet for them: DNZ downloads each list and sends it to the nodes that need it.
Lists are off by default, and a list does nothing by itself. A DNZ Proxy firewall rule names them, so
it applies to the routes you pick and costs one lookup per request only there (nothing measurable with a
list of 100,000 ranges, which takes a node about 8 MiB):
1
Add a rule to a firewall
In Proxy → Middlewares, open a firewall and add a rule with the condition Network list.
DNZ downloads a list as soon as a rule names it.
2
Pick what it does
A rule on a list challenges by default: the Under attack browser check, which real browsers
pass on their own and scripts don’t. It can block or only log instead.
3
Give the firewall to your routes
In the route dialog, or with batch edit.
{ "field": "list", "op": "in", "values": ["tor-exit", "spamhaus-drop"] }.
DNZ leaves out private and reserved ranges and anything wider than a /8 (/16 for IPv6), and keeps
the last good copy when a download fails. Lists are read by the node’s firewall, so a Traefik or
Caddy proxy has none. A list can’t be turned off while a rule names it.
Campaigns
The addresses the WAF matched, grouped by network (ASN), across hostnames, with what they did:AS4134 Chinanet: 3 addresses, 291 requests, 2 hostnames, Path traversal and SQL injectionWithout the ASN database (see Location data) they group by
/24 (/48 for IPv6). Times and hostnames
come from the latest events, so they may leave out some on a busy day. Open an address for its
details, or block the campaign’s addresses or their /24s: the same block as above. Blocking a whole
network by its ASN isn’t offered.
Bots
Decide what AI crawlers may do on a route, and catch visitors who only claim to be a search engine. Both are off by default and run on DNZ Proxy. Only routes that ask look at a request’s user agent: one with an AI crawler setting, or one whose firewall has a rule on bots. Every other route runs exactly as before and counts nothing.What a route lets AI crawlers do
GPTBot, ClaudeBot, CCBot, Bytespider, PerplexityBot and the others, by their user agent. Pick in the route dialog under Who gets in, in the Bots section of this page, or with--ai-crawlers:
For As robots.txt says, the node reads
/robots.txt from the site’s server after a crawler’s first
visit, never while a visit waits, and keeps it for an hour.
/robots.txt itself stays open to crawlers, in Block too, so a polite one can read that it isn’t
welcome. A firewall rule that allows the visit first wins over the setting.
A route with a setting other than Allow looks at every request’s user agent: about 3 µs of CPU on one CPU
(Proxy benchmarks). Allow costs nothing.
Google-Extended and Applebot-Extended are only words in
robots.txt: their requests say Googlebot
and Applebot, so no route can refuse them. Write them in your robots.txt.Verified bots
A visitor claiming Googlebot, Bingbot, Applebot, DuckDuckBot, YandexBot, Baiduspider, UptimeRobot or Pingdom is first treated as a plain visitor. DNZ checks the address in the background, against the ranges the vendor publishes or by forward-confirmed reverse DNS (the reverse name must be in the vendor’s domains, such as*.googlebot.com, and that name must point back to the address). The verdict holds for a day: verified or
fake, and a fake one’s log line says fake bot. None of it holds up a request.
If the check can’t finish (a resolver that fails, a list that can’t be read), there is no verdict and it stays a
plain visitor: a DNS failure never makes a real crawler fake. The same goes for a claim from a private address,
which is usually a forwarder you haven’t told the entry point to trust.
The names ship with DNZ, from each vendor’s own documentation, and reach the nodes only while a route or
firewall of that proxy uses them. Names DNZ knows in the section lists them all.
Rules on bots
Add a rule with the condition Bot to a firewall in Proxy → Middlewares. It takes verified, fake and AI crawler, with any action:The section
Humans here means what is left of the requests told apart: people, bots DNZ doesn’t know, and visitors whose claim hasn’t been checked yet. Routes that don’t tell bots apart count in none of these. Per hostname you see what its routes let AI crawlers do, and can change it there or for every route; each change is a version in route history. Click a fake bot’s address for its details.Preview a higher sensitivity
What would this route block at High? Let it run the extra rules in shadow mode for a while before you raise it. A preview is off by default, per route, and turns itself off after 24 hours. Start one with Preview High in the route dialog’s WAF group, under Previews of a higher sensitivity on this page, or in a batch edit. While it runs, the rules above the route’s sensitivity only count, with the noteshadow waf:<rule>:
- the visitor gets exactly what they got before;
- nothing shows as blocked or logged: not in the totals, the map, the chart, the attack alerts or a SIEM’s security events;
- no address gets closer to a ban.
So far High would also block 37 requests: 30 from addresses the WAF stopped nothing else from (mostly /search), which may be real visitors, and 7 from addresses it also stopped.Look at the first number before you switch: those may be real visitors. Only the busiest addresses are told apart, so the second number is at least. The events’ Preview only filter shows the requests themselves, from the log’s sample. Switch to High raises the route and ends the preview, and route history keeps a version. The end is a time kept with the route: neither a restart nor DNZ being away extends it, since each node reads the time itself. A node too old for previews ignores it, and the panel names it.
While it runs, a preview costs that route’s requests one more pass over the extra rules. Measured on one
CPU, a preview of High on a route at Medium cost a browser-like request about 5.5 µs (6% fewer requests a
second at the most a node handles) and a plain one about 2 µs. A route without a preview pays nothing more.
The weekly report
Every Monday at 08:00 in the server’s time zone, DNZ sends the seven days before it through the notification providers that ticked Weekly WAF report: requests blocked and logged, the busiest countries, rules and addresses, new bans, how many routes have the WAF, and the nodes that didn’t answer, whose numbers are missing from it. A new provider doesn’t get it until you tick it. Only the server that leads a cluster sends it, never a secondary. DNZ remembers which Monday it sent, so a restart or a new leader doesn’t send it twice. A server that was down at 08:00 still sends it, up to 48 hours late. Send it now on this page sends the last seven days as a test, which doesn’t count as Monday’s.Try a firewall rule on past traffic
Before you save a firewall rule, Try on past traffic in the rule editor says what it would have matched: how many requests, from how many addresses, with examples. Pick the last hour to the last 7 days. The rule is what’s in the editor, unsaved: nothing is written anywhere. It runs on the nodes, over each one’s request log, with the code the firewall itself runs, so a result can’t differ from the real rule. For a firewall that routes run, it looks only at those routes’ hostnames. What the rules above it stop never reaches it, as in the firewall. The log is a sample, and the answer says so. Each node keeps a part of what it serves (blocked requests get ten times the room):This rule would have matched 340 of the 5,210 lines in the logs’ sample, from 12 addresses. The logs hold 3.1% of the 168,000 requests served in this window, which comes to about 11,000 requests in all (an estimate).The estimate scales each hour and hostname by the requests the node counted, which are exact. It is an estimate and no more: a request the log keeps more often than others weighs differently. What the log can’t answer is named, never guessed. A log line holds the host, method, path with its query string, address, country, user agent and referer.
On a route that rewrites paths, a line may hold the rewritten path. Lines that were already blocked or challenged
count too, and are said apart.
It runs off the request path: one at a time on each node, at most 50,000 lines per node unless you ask for more
(200,000 at most), newest first, within 20 seconds. If it stops early the answer says where. A node too old to try
a rule isn’t asked and is named, as is one that didn’t answer. WAF exceptions and rate limits can’t be tried this
way yet.
When it wasn’t an attack
When the WAF stops a real visitor or app, use Not an attack… on its event and pick the narrowest exception:
It goes into the route’s WAF exceptions, and
route history keeps a version, so you can undo it there. When a
shield stopped it, the one choice is to turn that shield off on those routes.
Location data
- Countries on the map are counted on the nodes. Set a GeoIP database URL in the proxy’s DNZ Proxy settings, as for country rules in the firewall. Without one, DNZ places the busiest addresses with its own country database and says so above the map.
- Places and networks of addresses come from DNZ’s own country and ASN databases. Download fetches DB-IP’s free IP to Country Lite and IP to ASN Lite (CC BY 4.0); DNZ downloads them again every month. MaxMind GeoLite2 works too.
From the command line
proxy_waf_overview, proxy_waf_events, proxy_waf_address,
proxy_waf_coverage, proxy_waf_bans, proxy_waf_lists, proxy_waf_leaked_passwords, proxy_waf_campaigns and, with write
access, waf_block, waf_unblock, waf_exception, waf_ban and waf_unban. Downloading lists
and location databases is left to people. proxy_bot_traffic reads the Bots section and, with write
proxy_waf_coverage, proxy_waf_bans, proxy_waf_lists, proxy_waf_campaigns, proxy_waf_previews,
proxy_waf_report, try_firewall_rule and, with write access, waf_block, waf_unblock, waf_exception, waf_ban and
waf_unban; set_waf starts a preview. Downloading lists and location databases and sending the report
are left to people. proxy_bot_traffic reads the Bots section and, with write
access, set_ai_crawlers sets what AI crawlers may do.
dnz proxy waf shields —since 7d # each shield’s CVE, routes and hits; routes worth a look