Repository navigation
Say the whole command, so a machine can always rejoin - #85
Merged
Merged
Conversation
`tailscale up` refuses to run when the preferences already on the machine carry a non-default flag the command does not mention, and those preferences live in the ts-* volume, so they outlive the container. One `tailscale set --ssh` from the Tailnet policy panel was therefore enough to make every later join in the lab fail — at boot, on a configuration, on a reset, on a key rotation — with `make reset` the only way back, and no sign of why. Every `up` now carries --reset, which makes the command the whole statement of intent rather than a delta somebody can refuse. --reset also clears the one setting the lab means to keep, so setTailscaleSSH becomes the single place that owns Tailscale SSH: it sets it on every machine that is up and leaves the same fact in /lab/state/tailscale-ssh, which the entrypoint names explicitly on its own join. A machine restarted on its own now comes back the way the switch says it is. Measured on this host against tailscale 1.102.4 and headscale 0.29.3 on 2026-09-13: with RunSSH true and all three machines Stopped, applying `hardened` brings all three back to Running with RunSSH true; recreating all three containers has them join first time, no retries; all eleven audit checks pass afterwards.
17 of 26 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Target
develop: the normal case for a fix, a chapter, alab change, a typo
develop→main, shipping to the published guidemain, because published advice iscurrently locking readers out or leaving them exposed
What this changes
tailscale uprefuses to run at all when the preferences already on themachine carry a non-default flag the command does not mention, and those
preferences live in the
ts-*volume, so they outlive the container. Onetailscale set --sshfrom the Tailnet policy panel was therefore enough to makeevery later join in the lab fail — at boot, on a configuration, on a reset, on a
key rotation — with
make resetthe only way back and no sign of why.Every
upin the lab now carries--reset, so the command is the wholestatement of intent rather than a delta something flipped an hour ago can
refuse.
--resetalso clears the one setting the lab means to keep, sosetTailscaleSSHbecomes the single place that owns Tailscale SSH: it sets iton every machine that is up, and leaves the same fact in
/lab/state/tailscale-ssh, which the entrypoint names explicitly on its ownjoin. A machine restarted on its own now comes back the way the switch says it
is, instead of however
--resetleft it.Closes #74
Kind
If this changes advice or a command
These are the lab's own plumbing commands, not advice a reader runs. They do
appear in the script tab, so the two lines that changed there
(
tailscale up --reset …, andtailscale set --sshon a configuration) arenow what the server actually runs.
What I ran, and what it printed.
1 · The refusal, reproduced. All three machines
RunningwithRunSSHtrue— a refused command changes nothing, so this is a read-only reproduction:
2 · The configuration path, on the exact state in the issue.
day-onetotake them off, then
tailscale set --sshby hand to put the prefs back into thestate that breaks the join, then
hardened:Before this change that second
presetcall returned{"ok":true}and left allthree
Stopped. Tailscale SSH is on afterwards becausehardenedsays it is —the machines are not merely back, they are back in the state that was asked for.
3 · The boot path. Rebuilt the node image and recreated all three. The
ts-*volumes persist, so the machines booted carryingRunSSH: true:First attempt, no retries, and no
tailscale up failedline — the loop used toburn all five. The notice above it is tailscaled's own, printed because the
control server pushes the policy a moment later.
4 · The board afterwards. Nine of the eleven checks need a tailnet session,
which is what the issue said went quiet:
index.htmland
assets/sandbox.jsshowsudo tailscale upon a reader's own machineagainst the real coordination server, interactively and once.
--resetwould be wrong advice there: on a reader's machine it wipes preferences
they set on purpose. The hazard here is specific to a lab that re-runs
upunattended over prefs it did not write, so nothing outsidelab/changed.
the step — n/a, and the change is in the other direction: it removes the
state where nothing in the lab could get back in.
joinCmdinlab/control/actions.goand the one above the join inentrypoint.shboth say whatuprefuses and why the prefs outlive thecontainer.
Checks
make -C lab checkpassesgofmtis clean (make checkfails on any output at all)SPDX-License-Identifierheader — no new filesIf this touches the guide's pages
Not touched. No file outside
lab/changed.If this touches the lab or sandbox engine
dangerstill outranksokpass/fail
lab/README.mdmoved with it —no score moved; the scoring paths are untouched.
The one engine-adjacent path is the key-rotation set-piece, which now joins with
--resetand would otherwise have cleared Tailscale SSH behind the reader'sback mid-demonstration. It puts the setting back from the switch before anything
is measured, so the rotation stays a rotation and not a rotation plus a silent
change. The session it measures runs over
lab-vps's sshd, not Tailscale SSH,so the measurement itself is unaffected.
Licence
CC BY-SA 4.0 (guide) or GPL-3.0-or-later (code), per the file I touched
Not in this change
Two things I noticed and left alone, per one idea per branch:
assets/sandbox.js:1461models the switch assudo tailscale up --ssh/--ssh=false; the lab usestailscale set --ssh.Both are real Tailscale, but only one of them is the one that survives being
run twice — which is the whole of this bug. Worth a look at whether the
sandbox should show
set.day-onemay not be durable across a container restart. The entrypointjoins unconditionally whenever the pre-auth key is there
(
lab/images/node/entrypoint.sh:120), with nothing asking whether thecurrent configuration wants the machine on the tailnet. That predates this
change and is unrelated to the refusal. Untested — I read it, I did not
measure it.