name the control server explicitly, and log out before moving between them
Two gaps found by tracing the assembled command rather than assuming it, and the first meant option 3 did not work at all on a machine like this one. `tailscale up` with no --login-server keeps whatever ControlURL is already stored. So on a node already pointed at a self-hosted server — which this host is — choosing "the easy route" left it exactly where it was. No error, no message, and a summary line claiming it had connected. The URL is now passed explicitly in both cases, TS_DEFAULT_CONTROL_URL for Tailscale's own service. And a node logged in to one coordination server cannot simply be pointed at another; it has to be logged out first. That is now detected by comparing the stored URL with the target, and offered rather than done quietly — the tailnet drops while it happens, and the run says so, because on a machine reached over the tailnet that is the session you are reading this in. Declining leaves the node where it is and records that. Verified all three paths with tailscale stubbed: switching logs out then connects to controlplane.tailscale.com, declining leaves it on offscale, and reconnecting to the SAME server offers no logout at all. Also noted while tracing: lan_cidr correctly finds nothing on this host, since a /32 with host routes has no subnet to advertise. That means the homelab subnet-router prompt is the one path here that has not been exercised on real hardware. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -169,6 +169,22 @@ tailscale_control_url() {
|
||||
|
||||
tailscale_install() { curl -fsSL https://tailscale.com/install.sh | sh; }
|
||||
|
||||
# Tailscale's own coordination server, spelled out.
|
||||
#
|
||||
# Passed explicitly even when it is the default, because `tailscale up` with no
|
||||
# --login-server keeps whatever ControlURL is already stored. On a node already
|
||||
# pointed at a self-hosted server, choosing "the easy route" would otherwise
|
||||
# leave it exactly where it was — no error, no message, wrong answer.
|
||||
TS_DEFAULT_CONTROL_URL="https://controlplane.tailscale.com"
|
||||
|
||||
# Moving a node between coordination servers is not something `up` will do while
|
||||
# it is logged in to one. Logging out first is the documented way, and doing it
|
||||
# unasked would be worse than saying so.
|
||||
tailscale_needs_logout() {
|
||||
local current="$1" target="$2"
|
||||
[[ -n "$current" && -n "$target" && "$current" != "$target" ]]
|
||||
}
|
||||
|
||||
# Routing has to be on before this machine can forward anyone else's packets,
|
||||
# whether as an exit node or as a subnet router. Written as a drop-in so it is
|
||||
# visible as this script's doing.
|
||||
|
||||
@@ -530,11 +530,34 @@ if ! skip; then
|
||||
install_exit_node_tuning
|
||||
fi
|
||||
|
||||
# Where this node is being pointed, named explicitly in both cases — see
|
||||
# TS_DEFAULT_CONTROL_URL for why the default is not left implicit.
|
||||
if [[ "$TS_PLANE" == "self-hosted" ]]; then
|
||||
TS_TARGET_URL="$TS_LOGIN_SERVER"
|
||||
else
|
||||
TS_TARGET_URL="$TS_DEFAULT_CONTROL_URL"
|
||||
fi
|
||||
|
||||
# A node logged in to one coordination server cannot simply be pointed at
|
||||
# another; it has to be logged out first. Said out loud rather than done
|
||||
# quietly, because it drops the tailnet for a moment.
|
||||
TS_URL_NOW="$(tailscale_control_url)"
|
||||
if [[ "$TS_STATE" == "Running" ]] && tailscale_needs_logout "$TS_URL_NOW" "$TS_TARGET_URL"; then
|
||||
echo ""
|
||||
warn "this node is on ${TS_URL_NOW}, and moving it to ${TS_TARGET_URL} means logging out first"
|
||||
echo " The tailnet drops while that happens. If you are connected over"
|
||||
echo " it right now, this session goes with it."
|
||||
if confirm "Log out and move it?" n; then
|
||||
tailscale logout || true
|
||||
else
|
||||
warn "left where it is"
|
||||
SUMMARY+=("Tailscale: left on ${TS_URL_NOW}")
|
||||
TS_CONNECT=false
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
TS_ARGS=(up --timeout=60s)
|
||||
# Only a self-hosted server needs pointing at. Option 3 is the default
|
||||
# coordination server, which means passing no --login-server at all.
|
||||
[[ "$TS_PLANE" == "self-hosted" ]] && TS_ARGS+=(--login-server "$TS_LOGIN_SERVER")
|
||||
TS_ARGS=(up --timeout=60s --login-server "$TS_TARGET_URL")
|
||||
# Never passed empty. `--authkey ""` silently falls back to the interactive
|
||||
# flow and blocks forever, which is exactly how the original hung.
|
||||
[[ -n "$TS_AUTHKEY" ]] && TS_ARGS+=(--authkey "$TS_AUTHKEY")
|
||||
@@ -547,7 +570,9 @@ if ! skip; then
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if tailscale "${TS_ARGS[@]}"; then
|
||||
if [[ "$TS_CONNECT" == false ]]; then
|
||||
:
|
||||
elif tailscale "${TS_ARGS[@]}"; then
|
||||
TS_IP="$(tailscale_ip)"
|
||||
ok "connected as ${TS_IP} on $(hostname)"
|
||||
SUMMARY+=("Tailscale: ${TS_IP}${TS_SSH:+, Tailscale SSH}${TS_ROUTES:+, subnet router}${TS_EXIT:+, exit node}")
|
||||
|
||||
Reference in New Issue
Block a user