sshd: listen on the tsnet interface too#9
Merged
Conversation
net.Listen(":22") only binds the kernel network stack. tsnet is a
userspace WireGuard implementation with its own stack — connections to
the tailnet IP land there, not in the kernel, so the pivoted sshd was
unreachable from tailnet.
StartSSHServer now takes a TailnetListener (interface satisfied by
*tsnet.Server). When non-nil, it also binds :22 via tsnet.Listen,
alongside the kernel listener. Both listeners share the same
ServerConfig and accept loop.
tsnetauth splits into NewPostPivotServer (creates + Ups) and ServeSSH
(the ListenSSH path, unchanged). postpivot.Run holds the *tsnet.Server
so it can hand it to StartSSHServer as the TailnetListener.
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
net.Listen(\":22\")binds only the kernel network stack.tsnetis a userspace WireGuard implementation with its own stack; connections to the tailnet IP land there, not in the kernel. Result: the pivoted sshd from #6 wasn't reachable from tailnet clients — the port was open in the wrong place.StartSSHServernow takes aTailnetListener(interface satisfied by*tsnet.Server). When non-nil,:22is also bound viatsnet.Server.Listenand served by the sameServerConfigand accept loop as the kernel listener.tsnetauth.PostPivotsplits intoNewPostPivotServer(creates + Ups the server, returns it) andServeSSH(the unchangedListenSSHpath).PostPivotkept as a one-call wrapper for callers that don't need the server.postpivot.Runholds the*tsnet.Serverso it can pass it toStartSSHServeras theTailnetListener.Not calling
ServeSSHfromRunin this PR —tsnet.ListenSSH(\":22\")and our sshd'stsnet.Listen(\":22\")would fight for the same tailnet port. Bringing back Tailscale-native SSH is a separate PR (probably on a different port, or as a mode switch).Test plan
go test ./internal/postpivot/... ./internal/tsnetauth/... ./internal/config/... ./internal/cli/...on Linux (endeavour): all pass.TestStartSSHServerAddsTailnetListenerverifiesStartSSHServerbinds and cleans up when handed aTailnetListenerimplementation.nix build .#xmorphclean on endeavour.ssh root@<tailnet-ip>works from a mac App Store Tailscale client.