Skip to content

tsidp-server: add -login-server for custom control servers - #212

Open
RyoKusnadi wants to merge 1 commit into
tailscale:mainfrom
RyoKusnadi:feat/104-login-server
Open

RyoKusnadi wants to merge 1 commit into
tailscale:mainfrom
RyoKusnadi:feat/104-login-server

Conversation

@RyoKusnadi

Copy link
Copy Markdown

Fixes #104

Replaces #202 (closed by me; its fork branch was deleted, so it couldn't be reopened). Approved there by @mostlygeek; rebased on current main with no conflicts.

What

Adds a -login-server flag (env TS_LOGIN_SERVER) that sets tsnet.Server.ControlURL, so tsidp can join a tailnet run by a control server other than Tailscale's, such as Headscale. It works like tailscale up --login-server.

tsidp -login-server https://headscale.example.com
# or
TS_LOGIN_SERVER=https://headscale.example.com tsidp

Behavior

  • Empty (default): ControlURL is left unset, so tsnet behaves exactly as before, including honoring its own TS_CONTROL_URL env var. When -login-server is set, it takes precedence over TS_CONTROL_URL.

  • Precedence: the env var supplies the flag's default and an explicit flag wins, the same pattern as -advertise-tags and -authkey-file.

  • Validation (parseLoginServer): the value must be an absolute http:// or https:// URL with a host. It rejects:

    • credentials (https://user:pass@host, or any @), so they can't end up in logs
    • a query or fragment (?x=1, #f), which would break the URLs tsnet builds on top of it
    • a missing host (https://, https://:443) or an opaque form (https:host)

    On a bad value tsidp exits with code 1 and a fixed error message. Error messages never include any part of the input, so a mistyped URL that contains a token or password isn't written to the logs.

  • Normalizing: the scheme is lowercased and trailing slashes are removed (HTTPS://hs.example.com// → https://hs.example.com). The host keeps its case.

  • With -use-local-tailscaled: the flag has no effect (the local tailscaled must be set up with tailscale up --login-server instead), so tsidp logs a warning, as it already does for -authkey-file.

README: added the flag to the CLI flags table, the env var list and the Docker env table.

Tests

TestParseLoginServer is table-driven with 32 cases:

  • Accepted: empty, whitespace only, https, http with port, IPv6 with port, path, trailing slashes, uppercase scheme, surrounding whitespace.
  • Rejected: missing scheme, ftp://, no host, port without host, opaque URL, unparsable URL, userinfo, username only, query, empty query, fragment, empty fragment, plus malformed credential URLs (https:user:secret@host, user:secret@host, https://host:secret, …).
  • Every rejected case also asserts that the error text doesn't contain the input's secret.
go build ./...                 # ok
go vet . ./server/             # ok
go test -count 1 . ./server    # ok (260 tests, 2 packages)

golangci-lint reports two errcheck findings (f.Close in license_test.go, httpServer.Serve in tsidp-server.go); both are on lines this PR doesn't touch.

Not run: a live tailnet or Headscale server, and the built binary by hand this time (I did that on the original PR before the rebase).

Environment: main @ 43c68a6, go1.27.3 (what go.mod asks for), darwin/arm64.

Not covered

As noted in the issue thread, tsidp serves HTTPS through ts.ListenTLS, which needs the control server to issue certificates. This PR only adds the control-server setting; it doesn't change how certificates are obtained, so tsidp may still be unable to serve HTTPS on control servers without certificate support. I didn't test against a live Headscale server.

The README notes that TS_ env names may move to TSIDP_ later. I used TS_LOGIN_SERVER to match the existing network settings (TS_HOSTNAME, TS_AUTHKEY_FILE, …) and the CLI's --login-server. Happy to rename it.

Add a -login-server flag, with TS_LOGIN_SERVER as its environment
variable, that sets tsnet.Server.ControlURL. This lets tsidp join a
tailnet run by a control server other than Tailscale's, such as
Headscale, the same way `tailscale up --login-server` does.

The value must be an absolute http:// or https:// URL with a host.
URLs with credentials, a query or a fragment are rejected, and error
messages never repeat the input so a mistyped URL can't leak a secret
into the logs. The scheme is lowercased and trailing slashes are
removed. When the flag is empty, tsnet keeps its default behavior,
including honoring TS_CONTROL_URL. With -use-local-tailscaled the flag
has no effect and a warning is logged, as for -authkey-file.

Fixes tailscale#104

Signed-off-by: Ryo Kusnadi <ryokusnadi@gmail.com>
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.

Add support for login-server for non-Tailscale control servers

1 participant