diff --git a/ipn/ipnlocal/ssh.go b/ipn/ipnlocal/ssh.go new file mode 100644 index 000000000..ff81e6f1b --- /dev/null +++ b/ipn/ipnlocal/ssh.go @@ -0,0 +1,48 @@ +// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux +// +build linux + +package ipnlocal + +import ( + "errors" + "io/ioutil" + "os" + + "golang.org/x/crypto/ssh" + "tailscale.com/envknob" +) + +var useHostKeys = envknob.Bool("TS_USE_SYSTEM_SSH_HOST_KEYS") + +func (b *LocalBackend) GetSSHHostKeys() ([]ssh.Signer, error) { + // TODO(bradfitz): generate host keys, at least as needed if + // an existing SSH server didn't put them on disk. But also + // because people may want tailscale-specific ones. For now be + // lazy and reuse the host ones. + return b.getSystemSSHHostKeys() +} + +func (b *LocalBackend) getSystemSSHHostKeys() (ret []ssh.Signer, err error) { + for _, typ := range []string{"rsa", "ecdsa", "ed25519"} { + hostKey, err := ioutil.ReadFile("/etc/ssh/ssh_host_" + typ + "_key") + if os.IsNotExist(err) { + continue + } + if err != nil { + return nil, err + } + signer, err := ssh.ParsePrivateKey(hostKey) + if err != nil { + return nil, err + } + ret = append(ret, signer) + } + if len(ret) == 0 { + return nil, errors.New("no system SSH host keys found") + } + return ret, nil +} diff --git a/ssh/tailssh/tailssh.go b/ssh/tailssh/tailssh.go index de1dcce5e..28c0f9f6b 100644 --- a/ssh/tailssh/tailssh.go +++ b/ssh/tailssh/tailssh.go @@ -12,7 +12,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net" "os" "os/exec" @@ -21,7 +20,6 @@ import ( "github.com/creack/pty" "github.com/gliderlabs/ssh" - gossh "golang.org/x/crypto/ssh" "inet.af/netaddr" "tailscale.com/envknob" "tailscale.com/ipn/ipnlocal" @@ -35,14 +33,6 @@ import ( // Handle handles an SSH connection from c. func Handle(logf logger.Logf, lb *ipnlocal.LocalBackend, c net.Conn) error { - hostKey, err := ioutil.ReadFile("/etc/ssh/ssh_host_ed25519_key") - if err != nil { - return err - } - signer, err := gossh.ParsePrivateKey(hostKey) - if err != nil { - return err - } sshd := &server{lb, logf} srv := &ssh.Server{ Handler: sshd.handleSSH, @@ -59,7 +49,13 @@ func Handle(logf logger.Logf, lb *ipnlocal.LocalBackend, c net.Conn) error { for k, v := range ssh.DefaultSubsystemHandlers { srv.SubsystemHandlers[k] = v } - srv.AddHostKey(signer) + keys, err := lb.GetSSHHostKeys() + if err != nil { + return err + } + for _, signer := range keys { + srv.AddHostKey(signer) + } srv.HandleConn(c) return nil