Files
MalinandClaude Opus 5.5 909e085384 fix: run as service user via rc.subr, quieter logs, drop unreachable resolvers
daemon -u fails inside a jail (setusercontext), py312-sqlite3 is a
separate package, and FDN / Quad101 do not answer from gringo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 12:59:01 +02:00

51 lines
3.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# zonemaster-tools
The JSON API behind the DNS tools on **zonemaster.es**, the pages next to the
Zonemaster test. The GUI lives in `CloudHost/zonemaster-gui-es`, and Zonemaster
itself is in the stock backend.
| Endpoint (`/tools-api/…`) | What it does |
|---|---|
| `propagation?domain=&type=` | Sends the same question to 18 public resolvers worldwide, plus Google with an EDNS Client Subnet from 11 regions. Compares the answers against the zone's authoritative answer, found by walking down from the root. |
| `resolvers?domain=&type=&resolver=` | Asks 11 resolvers over UDP, TCP and DoH (HTTP/2). Reports latency, rcode and DNSSEC validation (AD bit). `resolver` adds a public IP of your choice. |
| `security?domain=` | 14 checks: DNSSEC (asked of four validators), NS diversity, SOA serial consistency, open AXFR, SPF (with RFC 7208 lookup count), DMARC, DKIM (35 common selectors), MX, CAA, MTA-STS, TLS-RPT, BIMI, dangling CNAMEs / takeover-prone targets, and wildcard. Returns a 0–100 score and grade, and stores one history row per change. |
| `security/history?domain=` | The tracker: past scores and per-check statuses. |
| `infra?domain=` | CDN/WAF/hosting from CNAMEs and HTTP headers, ASN per IP (Team Cymru), PTR, TLS certificate, and DNS and mail provider. |
| `graph?domain=` | Nodes and edges: the delegation path from the root, then NS/MX/web names, CNAME chains, IPs and ASNs. |
| `blocklists?domain=` | Checks 7 Pi-hole/AdGuard lists (AdGuard DNS, StevenBlack, HaGeZi PRO and TIF, OISD small, URLhaus, Phishing Army) the way each blocker matches them, plus 8 filtering resolvers asked live. |
| `blacklists?domain=` | Checks 6 domain DNSBLs (Spamhaus DBL, SURBL, URIBL…) and 10 IP DNSBLs for each web and mail IP. Queries go straight to each list's own nameservers. |
All output is language-neutral: ids, statuses and data, never prose. The GUI
writes the sentences in each locale.
## Guard rails
- **Only public addresses.** A user-supplied resolver, every probed IP and every HTTP connection must be `is_global`. The jail sits on `10.20.0.0/24` next to every other jail on gringo. `tests/test_parsers.py` pins this.
- **Rate limit:** 30 cost units per client per minute (a blacklist check costs 3), and 12 heavy requests in flight overall. The client comes from `X-Real-IP`, set by the host nginx.
- **Cache:** 20 s for propagation and resolvers, 120 s for everything else.
- **The web probe** sends one GET and reads headers only. It never follows a redirect or reads a body.
## Run
```sh
pkg install python312 py312-sqlite3 py312-dnspython py312-httpx py312-h2
cp -R zmtools /usr/local/zmtools/
cp rc.d/zmtools /usr/local/etc/rc.d/zmtools
pw useradd zmtools -d /var/db/zmtools -s /usr/sbin/nologin
sysrc zmtools_enable=YES zmtools_host=10.20.0.22
service zmtools start
```
State lives in `/var/db/zmtools/`:
- `blocklists.sqlite`: about 80 MB, rebuilt every 12 h beside the live copy and then swapped in.
- `history.sqlite`: the tracker's history.
The log is `/var/log/zmtools.log`.
## Test
```sh
python3 -m pytest -q tests # offline: parsing, validation, matching rules
```