mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-21 06:01:42 +00:00
tailcfg, ipn/ipnlocal: add Hostinfo.SSH_HostKeys, send when SSH enabled
(The name SSH_HostKeys is bad but SSHHostKeys is worse.) Updates #3802 Change-Id: I2a889019c9e8b065b668dd58140db4fcab868a91 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
4609096271
commit
bb93e29d5c
@ -894,7 +894,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
if b.inServerMode || runtime.GOOS == "windows" {
|
if b.inServerMode || runtime.GOOS == "windows" {
|
||||||
b.logf("Start: serverMode=%v", b.inServerMode)
|
b.logf("Start: serverMode=%v", b.inServerMode)
|
||||||
}
|
}
|
||||||
applyPrefsToHostinfo(hostinfo, b.prefs)
|
b.applyPrefsToHostinfo(hostinfo, b.prefs)
|
||||||
|
|
||||||
b.setNetMapLocked(nil)
|
b.setNetMapLocked(nil)
|
||||||
persistv := b.prefs.Persist
|
persistv := b.prefs.Persist
|
||||||
@ -1739,7 +1739,7 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) {
|
|||||||
|
|
||||||
oldHi := b.hostinfo
|
oldHi := b.hostinfo
|
||||||
newHi := oldHi.Clone()
|
newHi := oldHi.Clone()
|
||||||
applyPrefsToHostinfo(newHi, newp)
|
b.applyPrefsToHostinfo(newHi, newp)
|
||||||
b.hostinfo = newHi
|
b.hostinfo = newHi
|
||||||
hostInfoChanged := !oldHi.Equal(newHi)
|
hostInfoChanged := !oldHi.Equal(newHi)
|
||||||
userID := b.userID
|
userID := b.userID
|
||||||
@ -2444,13 +2444,23 @@ func unmapIPPrefixes(ippsList ...[]netaddr.IPPrefix) (ret []netaddr.IPPrefix) {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyPrefsToHostinfo(hi *tailcfg.Hostinfo, prefs *ipn.Prefs) {
|
// Warning: b.mu might be held. Currently (2022-02-17) both callers hold it.
|
||||||
|
func (b *LocalBackend) applyPrefsToHostinfo(hi *tailcfg.Hostinfo, prefs *ipn.Prefs) {
|
||||||
if h := prefs.Hostname; h != "" {
|
if h := prefs.Hostname; h != "" {
|
||||||
hi.Hostname = h
|
hi.Hostname = h
|
||||||
}
|
}
|
||||||
hi.RoutableIPs = append(prefs.AdvertiseRoutes[:0:0], prefs.AdvertiseRoutes...)
|
hi.RoutableIPs = append(prefs.AdvertiseRoutes[:0:0], prefs.AdvertiseRoutes...)
|
||||||
hi.RequestTags = append(prefs.AdvertiseTags[:0:0], prefs.AdvertiseTags...)
|
hi.RequestTags = append(prefs.AdvertiseTags[:0:0], prefs.AdvertiseTags...)
|
||||||
hi.ShieldsUp = prefs.ShieldsUp
|
hi.ShieldsUp = prefs.ShieldsUp
|
||||||
|
|
||||||
|
var sshHostKeys []string
|
||||||
|
if prefs.RunSSH {
|
||||||
|
// 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.
|
||||||
|
sshHostKeys = b.getSSHHostKeyPublicStrings()
|
||||||
|
}
|
||||||
|
hi.SSH_HostKeys = sshHostKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
// enterState transitions the backend into newState, updating internal
|
// enterState transitions the backend into newState, updating internal
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
@ -18,15 +19,16 @@ import (
|
|||||||
|
|
||||||
var useHostKeys = envknob.Bool("TS_USE_SYSTEM_SSH_HOST_KEYS")
|
var useHostKeys = envknob.Bool("TS_USE_SYSTEM_SSH_HOST_KEYS")
|
||||||
|
|
||||||
func (b *LocalBackend) GetSSHHostKeys() ([]ssh.Signer, error) {
|
func (b *LocalBackend) GetSSH_HostKeys() ([]ssh.Signer, error) {
|
||||||
// TODO(bradfitz): generate host keys, at least as needed if
|
// TODO(bradfitz): generate host keys, at least as needed if
|
||||||
// an existing SSH server didn't put them on disk. But also
|
// an existing SSH server didn't put them on disk. But also
|
||||||
// because people may want tailscale-specific ones. For now be
|
// because people may want tailscale-specific ones. For now be
|
||||||
// lazy and reuse the host ones.
|
// lazy and reuse the host ones.
|
||||||
return b.getSystemSSHHostKeys()
|
return b.getSystemSSH_HostKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) getSystemSSHHostKeys() (ret []ssh.Signer, err error) {
|
func (b *LocalBackend) getSystemSSH_HostKeys() (ret []ssh.Signer, err error) {
|
||||||
|
// TODO(bradfitz): cache this?
|
||||||
for _, typ := range []string{"rsa", "ecdsa", "ed25519"} {
|
for _, typ := range []string{"rsa", "ecdsa", "ed25519"} {
|
||||||
hostKey, err := ioutil.ReadFile("/etc/ssh/ssh_host_" + typ + "_key")
|
hostKey, err := ioutil.ReadFile("/etc/ssh/ssh_host_" + typ + "_key")
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@ -46,3 +48,11 @@ func (b *LocalBackend) getSystemSSHHostKeys() (ret []ssh.Signer, err error) {
|
|||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *LocalBackend) getSSHHostKeyPublicStrings() (ret []string) {
|
||||||
|
signers, _ := b.GetSSH_HostKeys()
|
||||||
|
for _, signer := range signers {
|
||||||
|
ret = append(ret, strings.TrimSpace(string(ssh.MarshalAuthorizedKey(signer.PublicKey()))))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
12
ipn/ipnlocal/ssh_stub.go
Normal file
12
ipn/ipnlocal/ssh_stub.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
func (b *LocalBackend) getSSHHostKeyPublicStrings() []string {
|
||||||
|
return nil
|
||||||
|
}
|
@ -49,7 +49,7 @@ func Handle(logf logger.Logf, lb *ipnlocal.LocalBackend, c net.Conn) error {
|
|||||||
for k, v := range ssh.DefaultSubsystemHandlers {
|
for k, v := range ssh.DefaultSubsystemHandlers {
|
||||||
srv.SubsystemHandlers[k] = v
|
srv.SubsystemHandlers[k] = v
|
||||||
}
|
}
|
||||||
keys, err := lb.GetSSHHostKeys()
|
keys, err := lb.GetSSH_HostKeys()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -454,6 +454,7 @@ type Hostinfo struct {
|
|||||||
RequestTags []string `json:",omitempty"` // set of ACL tags this node wants to claim
|
RequestTags []string `json:",omitempty"` // set of ACL tags this node wants to claim
|
||||||
Services []Service `json:",omitempty"` // services advertised by this machine
|
Services []Service `json:",omitempty"` // services advertised by this machine
|
||||||
NetInfo *NetInfo `json:",omitempty"`
|
NetInfo *NetInfo `json:",omitempty"`
|
||||||
|
SSH_HostKeys []string `json:"sshHostKeys,omitempty"` // if advertised
|
||||||
|
|
||||||
// NOTE: any new fields containing pointers in this type
|
// NOTE: any new fields containing pointers in this type
|
||||||
// require changes to Hostinfo.Equal.
|
// require changes to Hostinfo.Equal.
|
||||||
@ -516,6 +517,10 @@ func (v HostinfoView) RequestTags() views.StringSlice {
|
|||||||
return views.StringSliceOf(v.ж.RequestTags)
|
return views.StringSliceOf(v.ж.RequestTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v HostinfoView) SSH_HostKeys() views.StringSlice {
|
||||||
|
return views.StringSliceOf(v.ж.SSH_HostKeys)
|
||||||
|
}
|
||||||
|
|
||||||
func (v HostinfoView) Services() ServiceSlice {
|
func (v HostinfoView) Services() ServiceSlice {
|
||||||
return ServiceSliceOf(v.ж.Services)
|
return ServiceSliceOf(v.ж.Services)
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,7 @@ func (src *Hostinfo) Clone() *Hostinfo {
|
|||||||
dst.RequestTags = append(src.RequestTags[:0:0], src.RequestTags...)
|
dst.RequestTags = append(src.RequestTags[:0:0], src.RequestTags...)
|
||||||
dst.Services = append(src.Services[:0:0], src.Services...)
|
dst.Services = append(src.Services[:0:0], src.Services...)
|
||||||
dst.NetInfo = src.NetInfo.Clone()
|
dst.NetInfo = src.NetInfo.Clone()
|
||||||
|
dst.SSH_HostKeys = append(src.SSH_HostKeys[:0:0], src.SSH_HostKeys...)
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +127,7 @@ var _HostinfoCloneNeedsRegeneration = Hostinfo(struct {
|
|||||||
RequestTags []string
|
RequestTags []string
|
||||||
Services []Service
|
Services []Service
|
||||||
NetInfo *NetInfo
|
NetInfo *NetInfo
|
||||||
|
SSH_HostKeys []string
|
||||||
}{})
|
}{})
|
||||||
|
|
||||||
// Clone makes a deep copy of NetInfo.
|
// Clone makes a deep copy of NetInfo.
|
||||||
|
@ -32,7 +32,7 @@ func TestHostinfoEqual(t *testing.T) {
|
|||||||
"ShieldsUp", "ShareeNode",
|
"ShieldsUp", "ShareeNode",
|
||||||
"GoArch",
|
"GoArch",
|
||||||
"RoutableIPs", "RequestTags",
|
"RoutableIPs", "RequestTags",
|
||||||
"Services", "NetInfo",
|
"Services", "NetInfo", "SSH_HostKeys",
|
||||||
}
|
}
|
||||||
if have := fieldsOf(reflect.TypeOf(Hostinfo{})); !reflect.DeepEqual(have, hiHandles) {
|
if have := fieldsOf(reflect.TypeOf(Hostinfo{})); !reflect.DeepEqual(have, hiHandles) {
|
||||||
t.Errorf("Hostinfo.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
|
t.Errorf("Hostinfo.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
|
||||||
@ -181,6 +181,11 @@ func TestHostinfoEqual(t *testing.T) {
|
|||||||
&Hostinfo{},
|
&Hostinfo{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
&Hostinfo{SSH_HostKeys: []string{"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO.... root@bar"}},
|
||||||
|
&Hostinfo{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
got := tt.a.Equal(tt.b)
|
got := tt.a.Equal(tt.b)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user