Mail reverse DNS check, what Jarl adds to named.conf, lists one per line

jarld checks the reverse DNS of the public zones' MX and SPF addresses as the internet sees it, every 6 hours (Exposure page, jarl exposure mail, jarl_mail_reverse_dns_ok). The Server options page, jarl option generated and GET /options/generated list what Jarl writes into named.conf by itself; stats.rate_limit_log turns off the query-errors logging. Option lists of three or more elements are shown one per line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adamandClaude Opus 5.5 committed 2026-10-12 21:02:11 +01:00
1 parent d33f573782
commit c27a6c10bd
30 files changed
+1095 -16

No files matched your search

+36 -2
View File
@@ -107,7 +107,10 @@ is only in its log (category `query-errors`). That is why the activity
needs query statistics; without them, look at named's log for
`would rate limit`. When rate limiting is configured and statistics are
on, Jarl adds `query-errors` to its log channel (keeping named's syslog
destination for it).
destination for it). `stats.rate_limit_log: false` in jarl.yaml leaves
`query-errors` alone; the page then can't show what rate limiting does.
The Server options page lists this, and everything else Jarl adds to
named.conf by itself, under **Added by Jarl** (`jarl option generated`).
The statistics also count it: the `rrl` breakdown (`drop`, `slip`,
`would-drop`, `would-slip`), the `ratelimited` and `ratelimited_name` top
@@ -160,6 +163,36 @@ external`). Set in the server options, it applies to every view that has
none of its own, your internal resolver included, which RRL isn't meant
for.
## Mail
Receivers such as Gmail and Outlook check the address mail comes from: it
needs a PTR record (reverse DNS), and the name it gives must resolve back
to the address. Without that, mail is rejected or marked as spam. The PTR
records of your public addresses live in the reverse zones of whoever
owns the addresses, your hosting provider or ISP: you set them in their
panel, not in Jarl. A reverse zone you create in Jarl only answers your
own clients (unless the owner delegated it to you).
So Jarl checks them as the internet sees them, through the public
resolvers of `acme.resolvers`: the addresses of every public zone's MX
hosts (receiving) and SPF `ip4:`, `ip6:`, `a` and `a:` addresses
(sending). The Exposure page's **Mail reverse DNS** panel shows each
address, its PTR, who runs its reverse zone (the provider, from the
zone's name server) and what to do; jarld checks a minute after it starts
and every 6 hours, and logs an address that goes wrong.
```
$ jarl exposure mail
ZONE ADDRESS FOR PTR STATUS
adbraun.lol 172.235.168.106 SPF ip4, MX mail.adbraun.lol mail.adbraun.lol ok
```
It warns when an address has no PTR record, or when its name doesn't
resolve back to it; notes when the name is an alias (CNAME), or isn't the
MX name (fine if the mail server uses it in HELO). `jarl exposure mail`
exits 1 when an address isn't right; Prometheus has
`jarl_mail_reverse_dns_ok{zone,address}`.
## Confirming a change that opens a view up
A change that *introduces* an open resolver, open zone transfers, cache
@@ -204,7 +237,8 @@ monitoring, response size limits and usually a machine of its own.
| Method | Path | Body |
|--------|------|------|
| GET | `/exposure` | `scopes` (per view, or the server: `public`, `override`, `reason`, `rate_limit` off/log-only/enforcing, `findings` with `fix` where Jarl has one), `server` (server-wide findings), `rate_limiting` (last 24h, with statistics: `counts`, `networks`, `names`) |
| GET | `/exposure` | `scopes` (per view, or the server: `public`, `override`, `reason`, `rate_limit` off/log-only/enforcing, `findings` with `fix` where Jarl has one), `server` (server-wide findings), `rate_limiting` (last 24h, with statistics: `counts`, `networks`, `names`), `rate_limit_log`, `mail` (the last mail check) |
| POST | `/exposure/mail` | Checks the mail addresses' reverse DNS now: `checked`, `results` (`zone`, `address`, `roles`, `ptr`, `ok`, `level` ok/info/warning, `message`, `reverse_zone`, `managed_by`, `local_zone`), `errors` |
| POST | `/exposure/fix` | `{"view": "external", "code": "exposure_rate_limit"}` (`view_id` works too; no view: the server). Staged with `?changeset=` like any change |
| POST | `/exposure/ack` | `{"view": "external", "code": "security_open_resolver", "reason": "..."}` |
| POST | `/exposure/unack` | `{"view": "external", "code": "security_open_resolver"}` |