32349b7653
Rewrites the in-flight section, which had gone stale in one place (speaches is stopped, not live) and did not reflect a session in which everything opened was also closed. Three detail files carry the bulk: the FortiGate's public surface reduced to nothing including the ACME listener, Scriberr's deployment and the three upstream defects it required working around, and the ESH DNS repair that also made the IPv6 naming scheme real on three hosts. The tunnel entry in the decisions log described a cipher change as proposed and pending; it has since been attempted, found impossible in the form intended, and resolved differently, so the line now points at the outcome rather than the intention. New decisions cover the cipher adoption and the finding that the per-flow ceiling belongs to the UniFi gateway's software cipher rather than the firewall, the administrative closure, the transcription deployment, the DNS repair, the retirement of an ASR service whose only consumer was abandoned, and a database VM that until now had no fleet identity and no vaulted credential at all. Three approaches are recorded as abandoned: an AEAD cipher the far end cannot express, an upstream DNS setting the gateway accepts and ignores, and a scheme to claim unused delegated prefixes that founders not on the prefixes but on having to rebuild a firewall policy to use them.
51 lines
2.6 KiB
Markdown
51 lines
2.6 KiB
Markdown
# [2026-08-24] ana-gw public admin surface closed to zero, ACME listener included
|
||
|
||
WAN admin was opened at the start of the session as a cutover contingency
|
||
("so I don't have to drive down there"), then closed again on operator
|
||
instruction once the AES-128 work landed. Net result: **the FortiGate's WAN
|
||
address now exposes no TCP port at all.**
|
||
|
||
## Final state
|
||
|
||
External scan of `38.120.12.42`, 55 ports: **nothing open**. Verified from two
|
||
sites. `wan1 allowaccess` = `ping`; `infra-ops` trusthost back to `10.0.0.0/8`.
|
||
|
||
**Consequence to hold: there is no out-of-band path to ana-gw.** If both tunnels
|
||
drop it is console-only. Re-open is two one-liners (allowaccess + trusthost) —
|
||
both are recorded in auto-memory `reference_fortigate_ana_gw_access`.
|
||
|
||
## Port 80 was the FortiOS ACME listener, and I got it wrong first
|
||
|
||
`38.120.12.42:80` answered a bare 403 (`ACME Access Only`, 101 bytes) with
|
||
`allowaccess` set to ping only. First diagnosis — "an ISP transparent proxy" —
|
||
was **wrong**, and the reason is worth keeping:
|
||
|
||
> The sniffer filter was `dst host 38.120.12.42 and tcp port 80`. **`dst host`
|
||
> matches inbound only**, so outbound SYN-ACKs were excluded *by construction*,
|
||
> and concluding "the box sends no SYN-ACK" from that capture was unsound.
|
||
|
||
Re-run bidirectionally (`host … and tcp port 80`) it immediately showed
|
||
`wan1 out 38.120.12.42.80 -> <scanner>: syn ack`. **Rule: to test whether a box
|
||
*answers*, the filter must be bidirectional.**
|
||
|
||
The listener is opened by `config system acme / set interface "wan1"` and
|
||
**bypasses `allowaccess` by design** — FortiOS needs port 80 for HTTP-01. It
|
||
was disabled (`config system acme / unset interface`); the LE cert (`ana-fw.pfi`,
|
||
valid to 2026-10-27) is untouched and simply stops renewing, which is fine
|
||
because WAN admin is closed and the box is being replaced.
|
||
|
||
## Retracted in the same pass: the "four all-port VIPs" alarm
|
||
|
||
Claimed four VIPs were unrestricted all-port static NAT. **False.** A FortiOS
|
||
VIP is scoped **two** ways — `portforward`+`extport`, *or* a `service` binding
|
||
on the VIP object — and only the first was checked. All 14 VIPs are scoped;
|
||
`Rustdesk` is TCP 21115–21119, `ssh-mapped-2223` is TCP 2223 only.
|
||
|
||
Ground-truth external scan of all six public IPs is recorded in
|
||
`reference_fortigate_ana_gw_access`. Configured-but-dead: `:8443`
|
||
(mattermost-calls), `:8444` (webdav-nas), `:8880` (Kokoro-In) — tidy-up
|
||
candidates for the OPNsense translation, not exposure.
|
||
|
||
**Lesson, twice in one session: measure from outside instead of parsing config.**
|
||
Both wrong answers came from a filter that answered a different question.
|