mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-12 13:48:01 +00:00
assorted: plumb tka initialization & network-lock key into tailscaled
- A network-lock key is generated if it doesn't already exist, and stored in the StateStore. The public component is communicated to control during registration. - If TKA state exists on the filesystem, a tailnet key authority is initialized (but nothing is done with it for now). Signed-off-by: Tom DNetto <tom@tailscale.com>
This commit is contained in:
@@ -42,6 +42,7 @@ import (
|
||||
"tailscale.com/portlist"
|
||||
"tailscale.com/syncs"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tka"
|
||||
"tailscale.com/types/dnstype"
|
||||
"tailscale.com/types/empty"
|
||||
"tailscale.com/types/key"
|
||||
@@ -146,6 +147,8 @@ type LocalBackend struct {
|
||||
prefs *ipn.Prefs
|
||||
inServerMode bool
|
||||
machinePrivKey key.MachinePrivate
|
||||
nlPrivKey key.NLPrivate
|
||||
tka *tka.Authority
|
||||
state ipn.State
|
||||
capFileSharing bool // whether netMap contains the file sharing capability
|
||||
// hostinfo is mutated in-place while mu is held.
|
||||
@@ -997,6 +1000,9 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
||||
return fmt.Errorf("initMachineKeyLocked: %w", err)
|
||||
}
|
||||
}
|
||||
if err := b.initNLKeyLocked(); err != nil {
|
||||
return fmt.Errorf("initNLKeyLocked: %w", err)
|
||||
}
|
||||
|
||||
loggedOut := b.prefs.LoggedOut
|
||||
|
||||
@@ -1056,6 +1062,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
||||
// but it won't take effect until the next Start().
|
||||
cc, err := b.getNewControlClientFunc()(controlclient.Options{
|
||||
GetMachinePrivateKey: b.createGetMachinePrivateKeyFunc(),
|
||||
GetNLPublicKey: b.createGetNLPublicKeyFunc(),
|
||||
Logf: logger.WithPrefix(b.logf, "control: "),
|
||||
Persist: *persistv,
|
||||
ServerURL: b.serverURL,
|
||||
@@ -1515,6 +1522,21 @@ func (b *LocalBackend) createGetMachinePrivateKeyFunc() func() (key.MachinePriva
|
||||
}
|
||||
}
|
||||
|
||||
func (b *LocalBackend) createGetNLPublicKeyFunc() func() (key.NLPublic, error) {
|
||||
var cache atomic.Value
|
||||
return func() (key.NLPublic, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if v, ok := cache.Load().(key.NLPublic); ok {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
pub := b.nlPrivKey.Public()
|
||||
cache.Store(pub)
|
||||
return pub, nil
|
||||
}
|
||||
}
|
||||
|
||||
// initMachineKeyLocked is called to initialize b.machinePrivKey.
|
||||
//
|
||||
// b.prefs must already be initialized.
|
||||
@@ -1573,6 +1595,45 @@ func (b *LocalBackend) initMachineKeyLocked() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// initNLKeyLocked is called to initialize b.nlPrivKey.
|
||||
//
|
||||
// b.prefs must already be initialized.
|
||||
// b.stateKey should be set too, but just for nicer log messages.
|
||||
// b.mu must be held.
|
||||
func (b *LocalBackend) initNLKeyLocked() (err error) {
|
||||
if !b.nlPrivKey.IsZero() {
|
||||
// Already set.
|
||||
return nil
|
||||
}
|
||||
|
||||
keyText, err := b.store.ReadState(ipn.NLKeyStateKey)
|
||||
if err == nil {
|
||||
if err := b.nlPrivKey.UnmarshalText(keyText); err != nil {
|
||||
return fmt.Errorf("invalid key in %s key of %v: %w", ipn.NLKeyStateKey, b.store, err)
|
||||
}
|
||||
if b.nlPrivKey.IsZero() {
|
||||
return fmt.Errorf("invalid zero key stored in %v key of %v", ipn.NLKeyStateKey, b.store)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err != ipn.ErrStateNotExist {
|
||||
return fmt.Errorf("error reading %v key of %v: %w", ipn.NLKeyStateKey, b.store, err)
|
||||
}
|
||||
|
||||
// If we didn't find one already on disk, generate a new one.
|
||||
b.logf("generating new network-lock key")
|
||||
b.nlPrivKey = key.NewNLPrivate()
|
||||
|
||||
keyText, _ = b.nlPrivKey.MarshalText()
|
||||
if err := b.store.WriteState(ipn.NLKeyStateKey, keyText); err != nil {
|
||||
b.logf("error writing network-lock key to store: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
b.logf("network-lock key written to store")
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeServerModeStartState stores the ServerModeStartKey value based on the current
|
||||
// user and prefs. If userID is blank or prefs is blank, no work is done.
|
||||
//
|
||||
@@ -2437,6 +2498,14 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, prefs *ipn.Prefs, logf logger.Log
|
||||
return dcfg
|
||||
}
|
||||
|
||||
// SetTailnetKeyAuthority sets the key authority which should be
|
||||
// used for locked tailnets.
|
||||
//
|
||||
// It should only be called before the LocalBackend is used.
|
||||
func (b *LocalBackend) SetTailnetKeyAuthority(a *tka.Authority) {
|
||||
b.tka = a
|
||||
}
|
||||
|
||||
// SetVarRoot sets the root directory of Tailscale's writable
|
||||
// storage area . (e.g. "/var/lib/tailscale")
|
||||
//
|
||||
|
@@ -42,6 +42,7 @@ import (
|
||||
"tailscale.com/net/tsdial"
|
||||
"tailscale.com/safesocket"
|
||||
"tailscale.com/smallzstd"
|
||||
"tailscale.com/tka"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/groupmember"
|
||||
"tailscale.com/util/pidowner"
|
||||
@@ -770,6 +771,25 @@ func New(logf logger.Logf, logid string, store ipn.StateStore, eng wgengine.Engi
|
||||
return smallzstd.NewDecoder(nil)
|
||||
})
|
||||
|
||||
if root := b.TailscaleVarRoot(); root != "" {
|
||||
chonkDir := filepath.Join(root, "chonk")
|
||||
if _, err := os.Stat(chonkDir); err == nil {
|
||||
// The directory exists, which means network-lock has been initialized.
|
||||
chonk, err := tka.ChonkDir(chonkDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("opening tailchonk: %v", err)
|
||||
}
|
||||
authority, err := tka.Open(chonk)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("initializing tka: %v", err)
|
||||
}
|
||||
b.SetTailnetKeyAuthority(authority)
|
||||
logf("tka initialized at head %x", authority.Head())
|
||||
}
|
||||
} else {
|
||||
logf("network-lock unavailable; no state directory")
|
||||
}
|
||||
|
||||
dg := distro.Get()
|
||||
switch dg {
|
||||
case distro.Synology, distro.TrueNAS, distro.QNAP:
|
||||
|
@@ -34,6 +34,10 @@ const (
|
||||
// the server should start with the Prefs JSON loaded from
|
||||
// StateKey "user-1234".
|
||||
ServerModeStartKey = StateKey("server-mode-start-key")
|
||||
|
||||
// NLKeyStateKey is the key under which we store the nodes'
|
||||
// network-lock node key, in its key.NLPrivate.MarshalText representation.
|
||||
NLKeyStateKey = StateKey("_nl-node-key")
|
||||
)
|
||||
|
||||
// StateStore persists state, and produces it back on request.
|
||||
|
Reference in New Issue
Block a user