Skip to content

Harden the TCP bridge (port 7373): default-off + session token #53

Description

@marklynch

Problem

The TCP bridge (tcp_bridge.c, port 7373) requires no authentication and is the sharpest open surface on the device. The moment any LAN peer connects it:

  • streams every device log to the socket (via the esp_log_set_vprintf hook) — including info like the WiFi SSID — i.e. just connecting leaks data, and
  • accepts hex-string commands that write arbitrary frames to the pool bus.

It's also advertised over mDNS as _pool-bridge._tcp, so it's discoverable.

It is fundamentally a debug/diagnostics console used interactively (nc/telnet) or by scripts, so the goal is to secure it without breaking nc — no TLS, no per-session browser login.

Recommended approach

1. Default-off + enable from the (authenticated) web UI — baseline

Biggest win for ~zero friction, because of who uses it:

  • MQTT/Home Assistant users never touch 7373 → default-off costs them nothing.
  • Power users flip one toggle on the web config page; once on, nc poolcontrol-xxxx.local 7373 works as today.
  • Don't advertise _pool-bridge._tcp over mDNS when disabled (removes it from casual discovery/recon).

2. Token-gate the session when enabled

Low-friction static shared token sent as the first line (not a challenge/response handshake):

  • Generate a random token with esp_random() on first enable, store in NVS, display it on the authenticated web config page with a "rotate" button. Keep it separate from the admin password so it's safe to show and independently revocable.
  • On connect, prompt auth required and stream nothing / accept no commands until the matching token arrives. Constant-time compare; drop the connection on mismatch.
  • Crucially, do not enable log forwarding to the socket until after the token passes — this closes the connect-time log leak (today, connecting alone turns on the log stream).

Friction for genuine use: one paste for an interactive user (once per session), one extra line for a script:

printf '%s\n' "$BRIDGE_TOKEN" | cat - commands.txt | nc poolcontrol.local 7373

3. Optional: timed enable ("open for 15 min")

Make the web-UI enable a timed unlock that auto-disables, to minimise standing exposure. Nice-to-have on top of #1+#2, not a replacement.

Explicitly rejected (too much friction)

  • TLS on 7373 — kills nc.
  • Source-IP allowlist — brittle with DHCP.
  • Per-command auth / challenge-response — overkill on a LAN; first-line token is enough.

Recommended combination

Default-off + don't-advertise-when-off (#1) plus first-line token + no log stream until authenticated (#2). Zero friction for the MQTT majority, one-paste friction for the debug minority; closes both the bus-write and log-leak vectors. Add timed-enable (#3) later if desired.

Dependency

Requires the web-UI auth work (#52) to land first — the bridge token must be shown/rotated on a trusted (authenticated) config page, otherwise the gate is meaningless. Natural order: web auth → bridge default-off + token, reusing the same NVS-config and config-page plumbing.

Decisions to make

  1. Token storage format and rotation UX.
  2. Whether to also split log-forwarding into its own opt-in (it's the main leak vector even post-token).
  3. Include timed-enable now or defer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions