fix(tailscale): accept existing FQDN hostnames on the Linux join path

Open Beginner friendly
#16 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
linux, shell
Domain
backend

Research direction

Read skills/headless/scripts/linux/tailscale.sh and skills/headless/scripts/remote/tailscale.sh, especially ts_host() and the remote hostname validation. Compare the default keep-current-hostname path for a plain hostname and an FQDN on a disposable Linux target. Done when both supported names reach Tailscale's hostname or authentication path while unsupported input remains rejected.

Written by the indexing model from the issue text.

Description

Summary

The Linux Tailscale join path rejects a machine whose existing host name is an FQDN, even when the user chooses the default of keeping that name. The controller falls back to hostname, but the shared remote join routine rejects every dot before calling Tailscale. The current stable Tailscale CLI accepts the same FQDN.

Tested source and environment

Headless main at fe1a381c6f382ec3c9822da0e706484414bc63e0; Tailscale 1.104.5, the current stable release; disposable Ubuntu 26.04.1 LTS ARM64 VM through Crabbox 0.82.0. The test used the real Tailscale CLI and daemon and the unmodified headless remote join routine. No real auth key, device enrollment, or production tailnet was used.

Reproduction

The normal affected path is a Linux machine whose current hostname is, for example, headless.example.test, with hostName left unset to keep it and Tailscale enabled. After apply, scripts/linux/tailscale.sh CONFIG obtains that name from hostname and passes it to the remote join helper, which exits with headless: HOST is not a plain name.

The following focused comparison isolates the failed boundary. Run it only on a freshly installed disposable Linux target with Tailscale running and default preferences:

sudo tailscale up --hostname=headless.example.test \
  --login-server=http://127.0.0.1:1 --timeout=1s
sudo tailscale debug prefs
sudo bash skills/headless/scripts/remote/tailscale.sh \
  link headless.example.test -

The local closed control-server port prevents enrollment. The direct CLI call times out waiting for Running, while storing the requested FQDN as its hostname preference. The headless call rejects the hostname before starting authentication.

Observed results

Tailscale FQDN attempt exit=1
timeout waiting for Tailscale service to enter a Running state; check health with "tailscale status"
CLI stored Hostname=headless.example.test
Headless FQDN attempt exit=1
headless: HOST is not a plain name

The probe parsed the real tailscale debug prefs JSON and asserted that Hostname equals headless.example.test. The Tailscale timeout is expected for the deliberately closed local server and is not the reported defect. The failure is headless's earlier hostname rejection.

A further full-controller probe set the disposable target's actual hostname to headless.example.test, generated a configuration without --host-name, established key access, and invoked the unmodified scripts/linux/tailscale.sh CONFIG. It exited 1 with HOST is not a plain name followed by Tailscale gave no sign-in link, directly confirming the default keep-current-name path. The fixture used only a generated password and key and configured no production machine.

Expected behavior and source

Keeping a supported existing Linux hostname should not make the optional join step fail when Tailscale accepts that name. The default “keep the current one” choice should either be handled directly or transformed consistently into a valid Tailscale device name, rather than being rejected after configuration succeeds.

skills/headless/scripts/linux/tailscale.sh, ts_host(), uses the applied HOST_NAME or falls back to hostname verbatim. skills/headless/scripts/remote/tailscale.sh:22 then requires ^[A-Za-z0-9-]+$, which excludes dots. In contrast, Tailscale 1.104.5's ValidHostname checks each label of an FQDN, and tailscale up uses that validation.

This report covers the Linux fallback and shared remote validation boundary. A full interactive enrollment and native macOS/Windows behavior were not exercised. It is distinct from malformed JSON or newline-bearing key inputs: the rejected value is a normal existing DNS hostname.

Validation

Exercise the default keep-current-hostname path for a plain hostname and an FQDN. Both should reach Tailscale's supported hostname/authentication path without headless rejecting a valid name. Preserve rejection of shell syntax or other unsupported input. The direct CLI control establishes hostname acceptance, not successful enrollment; a reliable end-to-end workaround was not tested.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A valid existing FQDN flows through the Linux default hostname path but is rejected by the join helper, blocking optional Tailscale setup before authentication.
  • Workaround status: unverified
  • Workaround basis: The real CLI accepted and stored the FQDN, but no full authenticated enrollment or reliable end-to-end workaround was tested.
Dominant language
Shell
Stars
92
Forks
6
Avg merge
25m
Merged PRs (30d)
5

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from kunchenguid/headless

All issues in kunchenguid/headless

Similar issues

More Shell/Bash issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.