Skip to content

[Bug]: T3 Connect never reconciles on a server bound to a specific non-loopback --host since #34741 #36500

Open

Description

@jason5545

Before submitting

Area

apps/server

Steps to reproduce

  1. Run a headless server bound to a specific non-loopback address, for example its tailnet IP: t3 serve --host 100.x.y.z --port 3773 --no-browser (as a systemd service).
  2. Link it to T3 Connect (managed tunnel), so the desired link is stored.
  3. Restart the server.
  4. Run t3 connect status.

Expected behavior

The server reconciles its desired T3 Connect link on startup and the managed tunnel connects, as it did before #34741.

Actual behavior

Every startup logs Failed to reconcile T3 Connect desired link on startup with CloudLinkOriginInvalidError (origin: 'endpoint'), no tunnel starts, and t3 connect status reports T3 Connect is NOT reachable from other devices · The server has not set up a T3 Connect tunnel.

Cause: #34741 changed loopbackUrlHostForAddress (apps/server/src/startupAccess.ts) to return the bound address for a specific --host. Besides Tailscale Serve, two T3 Connect callers build the managed tunnel's local origin from it: the startup reconcile in server.ts (const localOrigin = …) and applyRelayConfig in cloud/CloudLink.ts. With a non-loopback bind the origin becomes http://100.x.y.z:3773, and isAllowedEndpointOrigin (cloud/linkChecks.ts) rejects any origin whose localHttpHost is not loopback, so the link proof is refused before it reaches the relay.

Before #34741 the origin was 127.0.0.1, so the link reconciled and the tunnel targeted loopback. That only reached the server when something listened there (we run a small loopback-to-tailnet forwarder for exactly this), so a non-loopback bind without one could not serve T3 Connect either way. It may be worth deciding which of the two the managed tunnel should target for a specific bind, and making the link checks agree with it.

Impact

Major degradation or frequent failure

Version or commit

Upstream main since 8ea4a88097 (#34741); still the same code at 580b526758.

Environment

Headless server on Ubuntu 24.04 x86_64, Node 26.12.1, run as a systemd service bound to its tailnet address. Two servers set up this way both lost T3 Connect on their first restart after updating past #34741.

Logs or stack traces

WARN: Failed to reconcile T3 Connect desired link on startup
  { cause: { _tag: 'CloudLinkOriginInvalidError', origin: 'endpoint' } }
✕ T3 Connect is NOT reachable from other devices
  The server has not set up a T3 Connect tunnel.

Screenshots, recordings, or supporting files

No response

Workaround

Keep the T3 Connect origin on loopback for a non-loopback bind and run a loopback listener that forwards to the server. In our fork, the two T3 Connect callers use 127.0.0.1 unless the bind address is loopback or a wildcard, while Tailscale Serve keeps #34741's behaviour; after that, both servers reconnected (✓ Other devices can reach this machine · Tunnel connected · 4 connections · QUIC).

#34741's test covers loopbackUrlHostForAddress alone; no test checks that the managed tunnel origin for a specific bind still passes isAllowedEndpointOrigin, which would have caught this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions