ipn/ipnlocal, feature/ssh: move SSH code out of LocalBackend to feature

This makes tsnet apps not depend on x/crypto/ssh and locks that in with a test.

It also paves the wave for tsnet apps to opt-in to SSH support via a
blank feature import in the future.

Updates #12614

Change-Id: Ica85628f89c8f015413b074f5001b82b27c953a9
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2026-03-10 21:33:12 +00:00
committed by Brad Fitzpatrick
parent 99e3e9af51
commit f905871fb1
23 changed files with 371 additions and 423 deletions
-23
View File
@@ -44,9 +44,6 @@ func init() {
// several candidate nodes is reachable and actually alive.
RegisterC2N("/echo", handleC2NEcho)
}
if buildfeatures.HasSSH {
RegisterC2N("/ssh/usernames", handleC2NSSHUsernames)
}
if buildfeatures.HasLogTail {
RegisterC2N("POST /logtail/flush", handleC2NLogtailFlush)
}
@@ -290,26 +287,6 @@ func handleC2NPprof(b *LocalBackend, w http.ResponseWriter, r *http.Request) {
c2nPprof(w, r, profile)
}
func handleC2NSSHUsernames(b *LocalBackend, w http.ResponseWriter, r *http.Request) {
if !buildfeatures.HasSSH {
http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotImplemented)
return
}
var req tailcfg.C2NSSHUsernamesRequest
if r.Method == "POST" {
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
res, err := b.getSSHUsernames(&req)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
writeJSON(w, res)
}
func handleC2NSockStats(b *LocalBackend, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
if b.sockstatLogger == nil {