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
+9 -7
View File
@@ -5646,10 +5646,12 @@ func (b *LocalBackend) applyPrefsToHostinfoLocked(hi *tailcfg.Hostinfo, prefs ip
// TODO(bradfitz): this is called with b.mu held. Not ideal.
// If the filesystem gets wedged or something we could block for
// a long time. But probably fine.
var err error
sshHostKeys, err = b.getSSHHostKeyPublicStrings()
if err != nil {
b.logf("warning: unable to get SSH host keys, SSH will appear as disabled for this node: %v", err)
if f, ok := feature.HookGetSSHHostKeyPublicStrings.GetOk(); ok {
var err error
sshHostKeys, err = f(b.TailscaleVarRoot(), b.logf)
if err != nil {
b.logf("warning: unable to get SSH host keys, SSH will appear as disabled for this node: %v", err)
}
}
}
hi.SSH_HostKeys = sshHostKeys
@@ -6439,9 +6441,9 @@ func (b *LocalBackend) maybeSentHostinfoIfChangedLocked(prefs ipn.PrefsView) {
}
}
// operatorUserName returns the current pref's OperatorUser's name, or the
// OperatorUserName returns the current pref's OperatorUser's name, or the
// empty string if none.
func (b *LocalBackend) operatorUserName() string {
func (b *LocalBackend) OperatorUserName() string {
b.mu.Lock()
defer b.mu.Unlock()
prefs := b.pm.CurrentPrefs()
@@ -6454,7 +6456,7 @@ func (b *LocalBackend) operatorUserName() string {
// OperatorUserID returns the current pref's OperatorUser's ID (in
// os/user.User.Uid string form), or the empty string if none.
func (b *LocalBackend) OperatorUserID() string {
opUserName := b.operatorUserName()
opUserName := b.OperatorUserName()
if opUserName == "" {
return ""
}