cmd/tailcat: use a short deterministic host for ssh's ControlPath - #15
Merged
bradfitz merged 1 commit intoAug 28, 2026
Merged
Conversation
Fixes #12. "tailcat ssh" passed the raw ConnBlob (the "user@<addrblob>" argument, potentially 100+ bytes once a full DERP Region is embedded rather than just a RegionID) straight through as the ssh destination hostname. ssh substitutes that literal hostname into %n for ControlPath (commonly "~/.ssh/master-%r@%n:%p"), and the expansion has to fit an AF_UNIX socket path -- about 104-108 bytes total, including the home directory. A long ConnBlob blows that budget on its own, so ControlMaster/ControlPath multiplexing failed with "too long for Unix domain socket" in an otherwise ordinary ssh config, before tailcat itself was even invoked. Give ssh a short, deterministic pseudo-hostname (sshDestHost: an 8-byte SHA-256 prefix of the blob, "tailcat-xxxxxxxxxxxxxxxx", 24 bytes fixed width) instead of the blob itself, while keeping the real blob flowing into ProxyCommand exactly as before -- that's still what does the actual connection routing, so nothing about how tailcat proxies the connection changes. Deterministic hashing (not truncation) matters here: ssh's connection sharing keys a control socket off ControlPath, so the same blob has to keep producing the same short host across invocations for multiplexing to correctly reuse (and not collide across) the right server's socket. Any "user@" prefix on the original argument is preserved and now applied to the short host instead of the raw blob, so -l/%r semantics for a real system sshd (via "tailcat --serve=22") are unaffected. TestSSHDestHost covers: deterministic output for the same blob, no collision between distinct blobs, fixed-width output, and that a realistic ControlPath built from the short host stays comfortably under the ~104-108 byte AF_UNIX limit. go build/vet/test ./... (default and -tags ts_omit_ssh) all pass.
bradfitz
approved these changes
Aug 28, 2026
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.
Summary
Fixes #12.
tailcat sshpassed the raw ConnBlob (theuser@<addrblob>argument — potentially 100+ bytes once a full DERPRegionis embedded rather than just aRegionID, seeConnInfointailcat.go) straight through as the destination hostname to the systemsshclient.sshsubstitutes that literal hostname into%nforControlPath(commonly~/.ssh/master-%r@%n:%p), and the expansion has to fit anAF_UNIXsocket path — about 104-108 bytes total, including the home directory. A longConnBlobblows that budget on its own, soControlMaster/ControlPathmultiplexing failed withunix_listener: path "..." too long for Unix domain socketin an otherwise completely ordinarysshconfig, beforetailcatwas even invoked — exactly the report in #12.Fix
sshDestHost()givesssha short, deterministic pseudo-hostname (an 8-byte SHA-256 prefix of the blob,tailcat-xxxxxxxxxxxxxxxx, 24 bytes fixed width) instead of the raw blob. The real blob is untouched: it still flows intoProxyCommandexactly as before, which is what actually does the connection routing — nothing about howtailcatproxies the connection changes, only the labelsshsees.Deterministic hashing (not truncation) matters here:
ssh's connection sharing keys a control socket offControlPath, so the same blob has to keep producing the same short host across invocations for multiplexing to correctly reuse — and not collide across — the right server's socket.Any
user@prefix on the original argument is preserved and now applied to the short host instead of the raw blob, so-l/%rsemantics for a real systemsshd(viatailcat --serve=22) are unaffected.Tests
TestSSHDestHost(new) covers:ControlPathbuilt from the short host stays comfortably under the ~104-108 byteAF_UNIXlimit.All pass (default and
ts_omit_sshbuild tags).Also manually verified the hash output shape/length against the reporter's math (a real
ConnBlobfixture already used elsewhere in this package's tests is 58 bytes; the fixed 24-byte replacement keeps a typicalControlPathwell under the limit regardless of how long the underlying blob is, which is the actual point — a blob-length-dependent truncation wouldn't have that guarantee).