Skip to content

fix(provider): bind dev oauth and custom-server to localhost by default#287

Merged
umputun merged 1 commit into
masterfrom
fix/dev-custom-bind-localhost
May 8, 2026
Merged

fix(provider): bind dev oauth and custom-server to localhost by default#287
umputun merged 1 commit into
masterfrom
fix/dev-custom-bind-localhost

Conversation

@paskal
Copy link
Copy Markdown
Collaborator

@paskal paskal commented May 8, 2026

Summary

provider/dev_provider.go and provider/custom_server.go listened with Addr: ":port", which binds to every interface. The dev OAuth UI and the embedded go-oauth2/oauth2 helper are intended for local development and embedded flows, not for LAN exposure -- but the bare-port shorthand silently exposed them to anyone who could route to the host's other addresses (other LAN clients, Docker network siblings, kubernetes pods on the same node, etc.).

Change

Add localBindAddr(host, port) in provider/service.go that defaults the listen host to 127.0.0.1 when the caller passes an empty host. Callers that explicitly want non-loopback exposure can pass a hostname:

  • dev_provider -- uses Provider.Host (existing field) as the host; empty -> 127.0.0.1, explicit "0.0.0.0"/"192.168.x.y" honored.
  • custom_server -- uses the host from c.URL (http://localhost:8080); "localhost" or empty -> 127.0.0.1; explicit non-loopback honored.

Same change in v1 and v2 in single PR.

README updated with a "Listen address" section documenting:

  • the security rationale for the localhost default;
  • how to opt back in to all-interfaces binding (AddDevProvider("0.0.0.0", port) for dev; URL: "http://0.0.0.0:port" for custom server);
  • the typical Docker case -- a server bound to 127.0.0.1 inside a container is not reachable from the host, so 0.0.0.0 is the right value when the auth process runs in a container.

Also fixed an outdated AddDevProvider(port) reference in the README -- the function takes (host, port).

Test

  • TestLocalBindAddr -- table covering empty / explicit-loopback / explicit-LAN / explicit-0.0.0.0 / IPv6.
  • Regression test: TestLocalBindAddr_DefaultIsNotAllInterfaces -- asserts the default bind never produces the ":port" shorthand. Reverting the helper to return ":" + port makes this test fail with: Should not be: ":12345" -- default bind must not be all-interfaces.

Both modules; go test -race ./... green; golangci-lint run --new-from-rev=master 0 issues.

Migration note

Existing deployments that intentionally exposed the dev OAuth helper or custom server beyond loopback need to set Provider.Host = "0.0.0.0" (dev) or change their Opts.URL host to 0.0.0.0/specific IP (custom-server). Default behaviour change is the security-relevant point of this PR.

@paskal paskal requested a review from umputun as a code owner May 8, 2026 18:51
@paskal paskal force-pushed the fix/dev-custom-bind-localhost branch from 0f43d5e to 52f6f3d Compare May 8, 2026 18:52
@coveralls
Copy link
Copy Markdown

coveralls commented May 8, 2026

Coverage Report for CI Build 25577691033

Coverage increased (+0.6%) to 84.827%

Details

  • Coverage increased (+0.6%) from the base build.
  • Patch coverage: 2 uncovered changes across 1 file (11 of 13 lines covered, 84.62%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
v2/provider/custom_server.go 7 5 71.43%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 3315
Covered Lines: 2812
Line Coverage: 84.83%
Coverage Strength: 7.76 hits per line

💛 - Coveralls

@paskal paskal force-pushed the fix/dev-custom-bind-localhost branch 3 times, most recently from ba9ee13 to 0455866 Compare May 8, 2026 20:15
provider/dev_provider.go and provider/custom_server.go used Addr ":port",
which listens on every interface. The dev OAuth UI and the embedded
go-oauth2/oauth2 helper are intended for local development and embedded
flows, not for LAN exposure — but the bare-port shorthand silently
exposed them to anyone who could route to the host's other addresses
(other LAN clients, Docker network siblings, kube pods, etc).

Add localBindAddr(host, port) in provider/service.go that defaults the
listen host to 127.0.0.1 when the caller passes an empty host. Callers
that explicitly want non-loopback exposure can pass a hostname:

* dev_provider — uses Provider.Host (existing field) as the host;
  empty -> 127.0.0.1, explicit "0.0.0.0"/"192.168.x.y" honored.
* custom_server — uses the host from c.URL ("http://localhost:8080");
  "localhost" or empty -> 127.0.0.1; explicit non-loopback honored.

Same change in v1 and v2 in single PR.

Tests in both modules:

* TestLocalBindAddr — table for empty/explicit-loopback/explicit-LAN/
  explicit-0.0.0.0/IPv6 cases.
* TestLocalBindAddr_DefaultIsNotAllInterfaces — regression test for
  the property "default bind never produces ':port' shorthand".
  Confirmed locally that reverting localBindAddr to "return ':' + port"
  makes this test fail with a clear message.
@paskal paskal force-pushed the fix/dev-custom-bind-localhost branch from 0455866 to c6c83bb Compare May 8, 2026 20:23
Copy link
Copy Markdown
Member

@umputun umputun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm. Nice catch on the bundled deadlock fix in CustomServer.Run: pre-existing, c.lock was being held through the URL-parse and SplitHostPort error returns, which would block Shutdown(). The regression test TestCustomServer_Run_BadURLDoesNotDeadlockShutdown is well-shaped.

one tiny note: the host == "localhost" normalization in custom_server.go:97 only normalizes the literal string. 127.0.0.1 and [::1] baked into c.URL would skip that branch, but they then go through localBindAddrnet.JoinHostPort, which produces a valid loopback bind. So no functional issue; just observing that the explicit "localhost" branch is needed only because localBindAddr treats empty as the default-loopback signal.

@umputun umputun merged commit 32d0c6e into master May 8, 2026
9 checks passed
@umputun umputun deleted the fix/dev-custom-bind-localhost branch May 8, 2026 23:00
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.

3 participants