Ports: each node checks the others' ports from outside #105

Merged
jcoffey-dev merged 1 commits from feature/port-reachability into main 2026-09-29 01:15:47 +00:00
Owner

The server side of the settings-reorg Ports item: is this port reachable from outside? This is option A, as John chose.

Why not just ask the server: a server connecting to its own public address never leaves the machine. It would pass whatever the firewall or provider in front of it blocks, and say "open" when the internet can't get in.

The cluster check:

  • Every 10 minutes, each node resolves every other active node's hostname (mail, mx2, mx3) through DNS, as a sender would. It then tries a TCP connection to each listener port on each address it gets back.
  • Only a connection is made; nothing is sent, so no session is logged and no rate limit counts it.
  • Results go in the shared in-memory store for an hour, keyed by target and prober, under the new prefix KV_PORT_REACHABILITY = 200. That's far above upstream's prefixes, so a new one of theirs can't collide.
  • The first round runs a minute after start, and only when the cluster coordinator is enabled.
  • Listeners bound only to loopback are left out.

GET /api/ports/check answers from whichever node the admin asks:

  • Cluster: {"mode":"cluster","ports":[…],"intervalSeconds":600,"nodes":[{"hostname":"mail.inbuxa.com","seenBy":[{"prober":"mx2.inbuxa.com","report":{"checked_at":…,"probes":[{"port":25,"address":"…","ok":true}]}}]}]}. report is null until a prober's first round.
  • Single server: there is no one outside to ask, so it only says whether each port is listening, using the bound address (or loopback for a wildcard bind): {"mode":"local","listening":[{"port":25,"address":"::1","listening":true}]}. The admin labels this as a local check.
  • Access: server-level administrators with SysNetworkListenerGet; tenants are refused.

Checked:

  • New unit tests: loopback-only listeners are left out and wildcard binds knock on loopback; probe_host reports an open port as ok and a closed one as not; a name that doesn't resolve says so.
  • The notice check is clean, and http and services check clean.
  • Against a local single server with two test listeners, /api/ports/check answered mode: local with the listening port true and the other false.
  • The cluster path can't run on one machine. Its first real run will be on prod after a release: within 11 minutes of the roll, each node should show the other two's view.

The admin's display follows in a separate PR.

The server side of the settings-reorg Ports item: *is this port reachable from outside?* This is option A, as John chose. **Why not just ask the server:** a server connecting to its own public address never leaves the machine. It would pass whatever the firewall or provider in front of it blocks, and say "open" when the internet can't get in. **The cluster check:** - Every 10 minutes, each node resolves every other active node's hostname (`mail`, `mx2`, `mx3`) through DNS, as a sender would. It then tries a TCP connection to each listener port on each address it gets back. - Only a connection is made; nothing is sent, so no session is logged and no rate limit counts it. - Results go in the shared in-memory store for an hour, keyed by target and prober, under the new prefix `KV_PORT_REACHABILITY = 200`. That's far above upstream's prefixes, so a new one of theirs can't collide. - The first round runs a minute after start, and only when the cluster coordinator is enabled. - Listeners bound only to loopback are left out. **`GET /api/ports/check`** answers from whichever node the admin asks: - **Cluster:** `{"mode":"cluster","ports":[…],"intervalSeconds":600,"nodes":[{"hostname":"mail.inbuxa.com","seenBy":[{"prober":"mx2.inbuxa.com","report":{"checked_at":…,"probes":[{"port":25,"address":"…","ok":true}]}}]}]}`. `report` is null until a prober's first round. - **Single server:** there is no one outside to ask, so it only says whether each port is listening, using the bound address (or loopback for a wildcard bind): `{"mode":"local","listening":[{"port":25,"address":"::1","listening":true}]}`. The admin labels this as a local check. - **Access:** server-level administrators with `SysNetworkListenerGet`; tenants are refused. **Checked:** - New unit tests: loopback-only listeners are left out and wildcard binds knock on loopback; `probe_host` reports an open port as ok and a closed one as not; a name that doesn't resolve says so. - The notice check is clean, and `http` and `services` check clean. - Against a local single server with two test listeners, `/api/ports/check` answered `mode: local` with the listening port true and the other false. - The cluster path can't run on one machine. Its first real run will be on prod after a release: within 11 minutes of the roll, each node should show the other two's view. The admin's display follows in a separate PR.
jcoffey-dev added 1 commit 2026-09-29 01:08:12 +00:00
Ports: each node checks the others' ports from outside
ci / fork-checks (pull_request) Successful in 16s
ci / build (pull_request) Successful in 7m26s
e35fc3e6d6
jcoffey-dev force-pushed feature/port-reachability from 7619bed638 to e35fc3e6d6 2026-09-29 01:08:12 +00:00 Compare
jcoffey-dev merged commit e99d26de89 into main 2026-09-29 01:15:47 +00:00
jcoffey-dev deleted branch feature/port-reachability 2026-09-29 01:15:47 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: inbuxa/inbuxa-server#105