mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-08 09:07:44 +00:00
cmd/tailscale: generate known_hosts file for 'tailscale ssh'
Updates #3802 Change-Id: I7a0052392f000ee44fc8e719f6666756aab91f3d Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
cceacda5eb
commit
df93158aac
@ -5,6 +5,7 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -12,13 +13,16 @@
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/alessio/shellescape"
|
"github.com/alessio/shellescape"
|
||||||
"github.com/peterbourgon/ff/v3/ffcli"
|
"github.com/peterbourgon/ff/v3/ffcli"
|
||||||
|
"tailscale.com/client/tailscale"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
|
"tailscale.com/ipn/ipnstate"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sshCmd = &ffcli.Command{
|
var sshCmd = &ffcli.Command{
|
||||||
@ -52,9 +56,21 @@ func runSSH(ctx context.Context, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
st, err := tailscale.Status(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
knownHostsFile, err := writeKnownHosts(st)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
argv := append([]string{
|
argv := append([]string{
|
||||||
ssh,
|
ssh,
|
||||||
|
|
||||||
|
"-o", fmt.Sprintf("UserKnownHostsFile %s",
|
||||||
|
shellescape.Quote(knownHostsFile),
|
||||||
|
),
|
||||||
"-o", fmt.Sprintf("ProxyCommand %s --socket=%s nc %%h %%p",
|
"-o", fmt.Sprintf("ProxyCommand %s --socket=%s nc %%h %%p",
|
||||||
shellescape.Quote(tailscaleBin),
|
shellescape.Quote(tailscaleBin),
|
||||||
shellescape.Quote(rootArgs.socket),
|
shellescape.Quote(rootArgs.socket),
|
||||||
@ -95,3 +111,52 @@ func runSSH(ctx context.Context, args []string) error {
|
|||||||
}
|
}
|
||||||
return errors.New("unreachable")
|
return errors.New("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeKnownHosts(st *ipnstate.Status) (knownHostsFile string, err error) {
|
||||||
|
confDir, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
tsConfDir := filepath.Join(confDir, "tailscale")
|
||||||
|
if err := os.MkdirAll(tsConfDir, 0700); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
knownHostsFile = filepath.Join(tsConfDir, "ssh_known_hosts")
|
||||||
|
want := genKnownHosts(st)
|
||||||
|
if cur, err := os.ReadFile(knownHostsFile); err != nil || !bytes.Equal(cur, want) {
|
||||||
|
if err := os.WriteFile(knownHostsFile, want, 0644); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return knownHostsFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func genKnownHosts(st *ipnstate.Status) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, k := range st.Peers() {
|
||||||
|
ps := st.Peer[k]
|
||||||
|
if len(ps.SSH_HostKeys) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// addEntries adds one line per each of p's host keys.
|
||||||
|
addEntries := func(host string) {
|
||||||
|
for _, hk := range ps.SSH_HostKeys {
|
||||||
|
hostKey := strings.TrimSpace(hk)
|
||||||
|
if strings.ContainsAny(hostKey, "\n\r") { // invalid
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&buf, "%s %s\n", host, hostKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ps.DNSName != "" {
|
||||||
|
addEntries(ps.DNSName)
|
||||||
|
}
|
||||||
|
if base, _, ok := strings.Cut(ps.DNSName, "."); ok {
|
||||||
|
addEntries(base)
|
||||||
|
}
|
||||||
|
for _, ip := range st.TailscaleIPs {
|
||||||
|
addEntries(ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user