Skip to content

feat(web): opt-in webHosts to reach the page over a private network - #4

Merged
matthewtsmith merged 1 commit into
mainfrom
feat/web-hosts
Sep 22, 2026
Merged

matthewtsmith merged 1 commit into
mainfrom
feat/web-hosts

Conversation

@matthewtsmith

Copy link
Copy Markdown
Member

Summary

Adds an optional webHosts field to foreman.json so the owner can open the status page from another machine on a trusted private network (for example http://mini:8090) without setting webAuth.

  • Each entry is a hostname or an IP literal and becomes an accepted Host at the page's port (IPv6 as [addr]:port, case-insensitive).
  • IP literals are also listened on, next to 127.0.0.1. Hostnames are never resolved or bound.
  • A failed extra bind (interface down, address not held) logs a warning; loopback keeps serving. Closing the page closes every listener.
  • With webAuth set, credentials still replace the Host check; webHosts only adds listen addresses.
  • Absent or empty, behaviour is unchanged: localhost only.

Security trade-off, documented in the README: anyone who can reach a listed address gets unauthenticated control of the page, so list only addresses on a private network you trust.

Known limitation

Extra addresses are bound once at daemon start. If the private network interface comes up later (e.g. after a reboot), that bind warns and is not retried until the daemon restarts.

Test plan

  • pnpm lint && pnpm typecheck && pnpm test && pnpm build green (598 tests)
  • Unit tests: Host allowlist (wrong port, bare name, suffix trick), IPv6 bracket form, listenAddresses dedupe, absent webHosts unchanged
  • Real-socket tests: listed Host returns 200, foreign Host 403; ::1 bind closes with the server; unbindable 192.0.2.1 warns while loopback serves
  • Dogfood: set webHosts on a local instance and open the page from another tailnet machine

🤖 Generated with Claude Code

https://claude.ai/code/session_01WhVzVMS6mftCFV735rT41b

webHosts lists hostnames and IP literals the page answers to without
webAuth. Every entry is an accepted Host at webPort (IPv6 in brackets);
IP literals are also listened on next to 127.0.0.1. Hostnames are never
resolved or bound. An extra address that cannot be bound is a warning.
Absent, the page stays localhost-only exactly as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WhVzVMS6mftCFV735rT41b
@matthewtsmith
matthewtsmith merged commit 2febeb3 into main Sep 22, 2026
1 check passed
@matthewtsmith
matthewtsmith deleted the feat/web-hosts branch September 22, 2026 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant