Skip to content

feat(config): default the dashboard bind to loopback - #39

Merged
enowdev merged 2 commits into
enowdev:mainfrom
hshinosa:feat/safer-default-host
Sep 15, 2026
Merged

enowdev merged 2 commits into
enowdev:mainfrom
hshinosa:feat/safer-default-host

Conversation

@hshinosa

Copy link
Copy Markdown
Collaborator

What

Route Server.Host in Default() through a resolver:

  1. ANTARES_HOST env var wins when non-empty — for installers, systemd units, and one-off invocations.
  2. Container heuristic (/.dockerenv, /run/.containerenv, or /proc/1/cgroup markers for docker/podman/containerd/kubepods) returns 0.0.0.0 so docker run -p 8787:8787 still works.
  3. Loopback fallback (127.0.0.1) everywhere else.

Why

A fresh install seeded server.host: 0.0.0.0, so the dashboard was reachable from every network interface (Wi-Fi, ethernet, VPN, hotspot) the moment the process started. ValidateListen then refused the first setup submit until the user configured an auth token or a dashboard password — blocking the wizard on the very first click, which is exactly when the user has nothing to configure yet.

The README already tells users the production binary "binds 127.0.0.1 by default" (see the "Accessing it from another machine" section). This PR brings the code into line with what the docs already promised.

Changes

  • internal/config/defaults.go: add defaultHost(), inContainer(), detectContainer(), and a containerProbe seam; route Server.Host in Default() through the resolver.
  • internal/config/defaults_test.go: new file, 8 subtests covering precedence (env override, container heuristic, loopback fallback, empty-env-falls-through, env-beats-container) plus a guard that Default() still routes through the resolver.
  • README.md: expand the "Accessing it from another machine" section with the container heuristic and ANTARES_HOST override; keep the existing note that non-loopback binds still require auth.

Not breaking

Existing configs with an explicit server.host keep their value — this only affects fresh installs and configs that omit the field. ValidateListen behavior unchanged: non-loopback binds still require server.auth_token, a dashboard password, or server.auth_disabled: true. Docker deployments unchanged (auto-switch to 0.0.0.0 inside containers).

Validation

  • GOTOOLCHAIN=go1.26.3 go test ./internal/config/... — 8 new subtests pass, existing runtime_test.go cases (which pass explicit Host: values) stay green.
  • GOTOOLCHAIN=go1.26.3 go vet ./internal/config/... clean.
  • Smoke: go run ./cmd/antares serve --foreground on macOS logs addr=127.0.0.1:8787 (was addr=0.0.0.0:8787).
  • Smoke: ANTARES_HOST=0.0.0.0 go run ./cmd/antares serve --foreground logs addr=0.0.0.0:8787 — override honored.

Follow-up (not in this PR)

Config.Validate() runs in runtimeServices.reload() and the setup save path but not on initial boot. A first run with ANTARES_HOST=0.0.0.0 and no auth currently starts the listener without tripping the guard — the guard only fires on the next reload or setup save. This gap predates this PR and is not made worse by it (defaults now bias toward loopback, so first-boot LAN exposure requires an explicit opt-in). Fix belongs in a small follow-up that calls cfg.Validate() before runtimeServices.start.

hshinosa and others added 2 commits September 15, 2026 10:06
The dashboard used to seed server.host with 0.0.0.0, so a fresh install
exposed the web UI on every network interface (Wi-Fi, ethernet, VPN,
hotspot). ValidateListen then refused the first setup submit until the
user configured an auth token or a dashboard password, blocking the
wizard on the very first click. The README already told users the
production binary "binds 127.0.0.1 by default" — this brings the code
into line with what was already documented.

Defaults now route through a resolver:
1. ANTARES_HOST wins when non-empty, so installers, systemd units, and
   one-off invocations can override without editing config.
2. Container heuristic returns 0.0.0.0 when /.dockerenv,
   /run/.containerenv, or /proc/1/cgroup marks Docker, Podman,
   containerd, or Kubernetes — otherwise `docker run -p 8787:8787`
   cannot reach the process.
3. Loopback fallback everywhere else.

Existing configs with an explicit server.host keep their value
untouched; this only changes what a fresh install writes and what a
config with no server.host resolves to. ValidateListen still refuses
non-loopback binds without auth, so opting in to LAN exposure still
requires the guard's consent.

Tests cover the three branches through a swappable containerProbe seam
so no container runtime is needed at test time.
@enowdev
enowdev merged commit 9e0cddb into enowdev:main Sep 15, 2026
3 checks passed
@enowdev

enowdev commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Fixed and merged with corrective commit 700c5cd. The original resolver included ANTARES_HOST in Default(), which is written to config.yaml on first boot; a one-off wildcard override therefore became permanent. Default() now seeds only the container/loopback value, and the existing environment overlay applies ANTARES_HOST afterward. Regression tests inspect saved YAML and reload after removing the variable, plus explicit YAML host precedence, whitespace-only values, and container seeding. README now explains that the container-derived seed is stored. Local config/CLI tests and GitHub go, web, and smoke checks passed. Closed through merge with the correction included.

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.

2 participants