ipn/ipnlocal: blend existing host SSH keys + newly-generated types as needed

If the host only has RSA, use its RSA + generate ecdsa + ed25519, etc.

Perhaps fixes https://twitter.com/colek42c/status/1550554439299244032 and
something else that was reported.

Change-Id: I88dc475c8e3d95b6f25288ff7664b8e72655fd16
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/5213/head
Brad Fitzpatrick 2 years ago committed by Brad Fitzpatrick
parent e5176f572e
commit eae003e56f

@ -26,6 +26,7 @@ import (
"github.com/tailscale/golang-x-crypto/ssh" "github.com/tailscale/golang-x-crypto/ssh"
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/util/mak"
) )
var useHostKeys = envknob.Bool("TS_USE_SYSTEM_SSH_HOST_KEYS") var useHostKeys = envknob.Bool("TS_USE_SYSTEM_SSH_HOST_KEYS")
@ -36,34 +37,39 @@ var useHostKeys = envknob.Bool("TS_USE_SYSTEM_SSH_HOST_KEYS")
var keyTypes = []string{"rsa", "ecdsa", "ed25519"} var keyTypes = []string{"rsa", "ecdsa", "ed25519"}
func (b *LocalBackend) GetSSH_HostKeys() (keys []ssh.Signer, err error) { func (b *LocalBackend) GetSSH_HostKeys() (keys []ssh.Signer, err error) {
var existing map[string]ssh.Signer
if os.Geteuid() == 0 { if os.Geteuid() == 0 {
keys, err = b.getSystemSSH_HostKeys() existing = b.getSystemSSH_HostKeys()
if err != nil || len(keys) > 0 {
return keys, err
}
// Otherwise, perhaps they don't have OpenSSH etc installed.
// Generate our own keys...
} }
return b.getTailscaleSSH_HostKeys() return b.getTailscaleSSH_HostKeys(existing)
} }
func (b *LocalBackend) getTailscaleSSH_HostKeys() (keys []ssh.Signer, err error) { // getTailscaleSSH_HostKeys returns the three (rsa, ecdsa, ed25519) SSH host
root := b.TailscaleVarRoot() // keys, reusing the provided ones in existing if present in the map.
if root == "" { func (b *LocalBackend) getTailscaleSSH_HostKeys(existing map[string]ssh.Signer) (keys []ssh.Signer, err error) {
return nil, errors.New("no var root for ssh keys") var keyDir string // lazily initialized $TAILSCALE_VAR/ssh dir.
}
keyDir := filepath.Join(root, "ssh")
if err := os.MkdirAll(keyDir, 0700); err != nil {
return nil, err
}
for _, typ := range keyTypes { for _, typ := range keyTypes {
if s, ok := existing[typ]; ok {
keys = append(keys, s)
continue
}
if keyDir == "" {
root := b.TailscaleVarRoot()
if root == "" {
return nil, errors.New("no var root for ssh keys")
}
keyDir = filepath.Join(root, "ssh")
if err := os.MkdirAll(keyDir, 0700); err != nil {
return nil, err
}
}
hostKey, err := b.hostKeyFileOrCreate(keyDir, typ) hostKey, err := b.hostKeyFileOrCreate(keyDir, typ)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("error creating SSH host key type %q in %q: %w", typ, keyDir, err)
} }
signer, err := ssh.ParsePrivateKey(hostKey) signer, err := ssh.ParsePrivateKey(hostKey)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("error parsing SSH host key type %q from %q: %w", typ, keyDir, err)
} }
keys = append(keys, signer) keys = append(keys, signer)
} }
@ -115,24 +121,21 @@ func (b *LocalBackend) hostKeyFileOrCreate(keyDir, typ string) ([]byte, error) {
return pemGen, err return pemGen, err
} }
func (b *LocalBackend) getSystemSSH_HostKeys() (ret []ssh.Signer, err error) { func (b *LocalBackend) getSystemSSH_HostKeys() (ret map[string]ssh.Signer) {
// TODO(bradfitz): cache this?
for _, typ := range keyTypes { for _, typ := range keyTypes {
filename := "/etc/ssh/ssh_host_" + typ + "_key" filename := "/etc/ssh/ssh_host_" + typ + "_key"
hostKey, err := ioutil.ReadFile(filename) hostKey, err := ioutil.ReadFile(filename)
if os.IsNotExist(err) || len(bytes.TrimSpace(hostKey)) == 0 { if err != nil || len(bytes.TrimSpace(hostKey)) == 0 {
continue continue
} }
if err != nil {
return nil, err
}
signer, err := ssh.ParsePrivateKey(hostKey) signer, err := ssh.ParsePrivateKey(hostKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("error reading private key %s: %w", filename, err) b.logf("warning: error reading host key %s: %v (generating one instead)", filename, err)
continue
} }
ret = append(ret, signer) mak.Set(&ret, typ, signer)
} }
return ret, nil return ret
} }
func (b *LocalBackend) getSSHHostKeyPublicStrings() (ret []string) { func (b *LocalBackend) getSSHHostKeyPublicStrings() (ret []string) {

@ -15,7 +15,7 @@ import (
func TestSSHKeyGen(t *testing.T) { func TestSSHKeyGen(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
lb := &LocalBackend{varRoot: dir} lb := &LocalBackend{varRoot: dir}
keys, err := lb.getTailscaleSSH_HostKeys() keys, err := lb.getTailscaleSSH_HostKeys(nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -32,7 +32,7 @@ func TestSSHKeyGen(t *testing.T) {
t.Fatalf("keys = %v; want %v", got, want) t.Fatalf("keys = %v; want %v", got, want)
} }
keys2, err := lb.getTailscaleSSH_HostKeys() keys2, err := lb.getTailscaleSSH_HostKeys(nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

Loading…
Cancel
Save