Regression: TS-2026-006 control absent from current stable and main
Severity (my assessment): none known standalone today (residual defenses hold — see below), but a security-bulletin fix regressed across patch releases of the same minor without announcement. Reported as a process/regression issue. Date found: 2026-10-24 · Reporter: psn · shared privately with Tailscale 2026-10-24
Summary
The digit-only SSH username rejection deployed for TS-2026-006 (“Tailscale now
disallows the use of UIDs or numeric-only usernames via SSH”, commit f368a96e,
2026-06-11) is present in v1.98.9 and v1.102.0, but is absent from v1.102.4 and from
main as of 2026-10-24.
Verification
In ssh/tailssh/tailssh.go, clientAuth used to contain, before setInfo:
uidRegex = regexp.MustCompile("^[0-9]+$")
...
if uidRegex.MatchString(cm.User()) {
return nil, c.errBanner(fmt.Sprintf("rejecting username %q. Usernames that consist of only digits are not allowed as they are ambiguous with numerical UIDs", cm.User()), nil)
}
Checked against raw.githubusercontent.com/tailscale/tailscale/<ref>/ssh/tailssh/tailssh.go:
| ref | uidRegex present? |
|---|---|
| v1.98.9 | yes |
| v1.102.0 | yes |
| v1.102.4 | no |
| main (2026-10-24) | no |
No commit in the public history of ssh/tailssh/tailssh.go advertises removing it; the
mid-September 2026 refactoring of that directory (e.g. 664ba588 “split the OS-specific
parts of the server into their own files”, 2026-09-15, and neighboring changes) falls in
the window. A git bisect start v1.102.4 v1.102.0 -- ssh/tailssh/ will pin it exactly.
Current residual defenses (why no known exploit today)
mapLocalUser’sautogroup:nonrootbranch (e48e7b73, 2026-07-24) resolves the requested user via getent and rejectslu.Uid == "0" || lu.Username == "root", so the classic0@hostis still refused.- getent input hardening rejects leading dashes (TS-2026-009’s class).
I could not construct a working root bypass in v1.102.4 that the removed check would have blocked — so this is defense-in-depth loss, not a demonstrated vulnerability. But:
- The TS-2026-006 bulletin tells customers the control exists; current stable doesn’t have it. Fleet audits against that bulletin’s claim will mis-assess.
- Its removal may interact with future NSS/getent edge cases the original fix was designed to foreclose (numeric ambiguity is exactly where lookup bypasses keep recurring: TS-2026-006, TS-2026-009).
Suggested remediation
- Restore the check on
mainand backport to the 1.102.x release branch (1.102.1+ shipped without it). - Add a regression test pinned to the bulletin number so refactors can’t silently drop it again (the existing fix shipped with a test; the test appears to have been dropped with the check).
- Consider a release-process rule: commits touching files that carry
security-fix references (bulletin IDs,
Fixes tailscale/corp#NNNN) get a second reviewer, and changelogs mention security-fix regressions.
Detection
For fleets: no client-visible signal. An integration test
(ssh -o User=0 target must be rejected with the “digits” banner) is the cheapest guard.