audit 4: DNS outages unknown, DMARC limits said, typed Health, one TLS mode per upstream
test / test (push) Canceled after 0s
test / test (push) Canceled after 0s
M14, M15: queryEach gives each resolver 2 s within the check's context and asks the one that answered last first; an address or PTR lookup not answered is Unknown, an answered mismatch beside it still an error; Checker.Server keeps the last answer's addresses, so SPF does not go Unknown with it. Notifications report faults only, now tested with SPF unknown alone and beside a wrong DKIM. DMARC reports: tests of malformed XML, gzip and zip bombs, a message over the limit, the service's Resync, hosted suggestion and validation. The finding: both limits cut silently and an oversized report failed as damaged; readAtMost refuses it for its size (MaxMessage 10 MiB, moved into IngestMessage). healthReadings replaces map[string]any for Health and Overview. L5: an inbound upstream has one TLS mode - a second domain to the same [host]:port with another mode is refused, naming the first; the TLS policy map holds each next hop once. Tested on the test server: go vet + go test of 25 packages, make e2e (all pass, 631 s) on this tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1326,7 +1326,7 @@ command whose leftover is the owner's alone.
|
||||
retried - the subject is gone with it, as before. Tests: a failed copy
|
||||
goes out at the next check, once; the ceiling holds one back; a user
|
||||
added mid-subject hears nothing of it, its end included.
|
||||
- [ ] **A DNS outage is "unknown", not "wrong"** (M14, M15; owner,
|
||||
- [x] **A DNS outage is "unknown", not "wrong"** (M14, M15; owner,
|
||||
2026-10-14 - the two findings had no step). Each resolver gets a time
|
||||
budget of its own, so a silent first resolver no longer leaves the second
|
||||
an expired context ([dnscheck/resolver.go](../internal/dnscheck/resolver.go));
|
||||
@@ -1338,7 +1338,17 @@ command whose leftover is the owner's alone.
|
||||
and tested. Tests: a first resolver that drops packets, a PTR timeout.
|
||||
architecture.md:339-340 and the `DefaultResolvers` comment say what is
|
||||
true. — **Opus**
|
||||
- [ ] **DMARC reports tested where they come in** (owner, 2026-10-14; rows of
|
||||
*Done 2026-10-15:* `queryEach` gives each resolver
|
||||
`perResolverTimeout` (2 s) within the check's context and asks the one
|
||||
that answered last first (a dropping first resolver costs one 2 s wait,
|
||||
then is asked last); an address or PTR lookup not answered is Unknown
|
||||
(`lookupFailed`), an answered mismatch beside it still the error it is;
|
||||
`Checker.Server` keeps the last answer's addresses when the lookup was
|
||||
not answered. `notify.dnsReading` already reported faults only - now
|
||||
tested with SPF unknown alone and beside a wrong DKIM. architecture.md
|
||||
no longer has the sentence the audit cited; the guide's
|
||||
`SELFPOST_DNS_RESOLVERS` row says the budget and the order.
|
||||
- [x] **DMARC reports tested where they come in** (owner, 2026-10-14; rows of
|
||||
review-2026-08-followups). The panel parses reports that arrive by mail
|
||||
from anyone, and the `internal/dmarc` service (`service.go`,
|
||||
`addresses.go`) and the DMARC handlers have no tests; no other step
|
||||
@@ -1346,18 +1356,37 @@ command whose leftover is the owner's alone.
|
||||
oversized, compressed past its limit, or names a domain the instance does
|
||||
not have: refused or skipped with a reason, nothing stored, the panel
|
||||
alive. A finding is fixed in this step. — **Opus**
|
||||
- [ ] **The last untyped page data** (owner, 2026-10-13): `healthChecks`
|
||||
*Done 2026-10-15.* Since the audit the package had gained tests (bounds,
|
||||
unknown domain, tag mismatch, not a report, a store failure, the
|
||||
allow-list) and the handlers theirs. Added: malformed XML, a gzip bomb
|
||||
and a zip bomb (9 MiB of zeroes), a message over the limit - each
|
||||
refused with its reason, nothing stored; the service's Resync on and
|
||||
off, the hosted suggestion and its validation. The finding: both limits
|
||||
cut silently (`io.LimitReader`), so an oversized report failed as
|
||||
damaged; `readAtMost` refuses it for its size (`MaxMessage` 10 MiB, the
|
||||
message limit moved from cmd/panel into `IngestMessage`). The panel
|
||||
is not in the path: ingest is its own process per message, and a
|
||||
refusal exits 0 with the reason in `mail.log`.
|
||||
- [x] **The last untyped page data** (owner, 2026-10-13): `healthChecks`
|
||||
([handlers_status.go](../internal/web/handlers/handlers_status.go)) returns
|
||||
a struct instead of `map[string]any`, so a mistyped key in `healthPage` /
|
||||
`healthCards` is a compile error and not an empty card on Server › Health.
|
||||
No template change. This closes what is left of the roadmap's former
|
||||
`template-data-typing`. — **Sonnet**
|
||||
- [ ] **Upstream TLS mode validated at save** (L5): saving an inbound domain
|
||||
*Done 2026-10-15:* `healthReadings`, read by `healthPage` and
|
||||
`healthCards` and the tests' fixtures; no `map[string]any` is left in
|
||||
internal/web outside tests.
|
||||
- [x] **Upstream TLS mode validated at save** (L5): saving an inbound domain
|
||||
whose `[host]:port` is already used by another domain with a different
|
||||
TLS mode is refused with the validator's text naming that domain and its
|
||||
mode; `TestRenderInboundMaps` gets the two-routes-one-next-hop case and the
|
||||
map is asserted to hold one key. Guide § Inbound domains says a next hop
|
||||
has one TLS mode. — **Opus**
|
||||
*Done 2026-10-15:* `inbound.Service.checkOneTLSModePerUpstream` (the
|
||||
domain itself may change its own mode; another port is another next
|
||||
hop); `renderInboundMaps` writes a next hop's policy line once (rows
|
||||
saved before keep the first by domain name, as Postfix did). Tests in
|
||||
inbound and postfix (`TestRenderInboundMapsOneTLSLinePerNextHop`).
|
||||
|
||||
### Stage 5 — image and Postfix hardening (M17–M19, M21, L30–L32) → `2.6.0`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user