From 53fb25fc2fc49deaae48e36044f80ff1eded9aa9 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 19 Jun 2020 12:06:49 -0700 Subject: [PATCH] all: generate discovery key, plumb it around Not actually used yet. Updates #483 --- control/controlclient/direct.go | 4 ++++ ipn/local.go | 5 +++++ tailcfg/tailcfg.go | 1 + types/key/key.go | 11 +++++++++++ wgengine/magicsock/magicsock.go | 10 ++++++++++ wgengine/userspace.go | 4 ++++ wgengine/watchdog.go | 4 ++++ wgengine/wgengine.go | 6 ++++++ 8 files changed, 45 insertions(+) diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index b6cc66939..3ffeaba87 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -85,6 +85,7 @@ type Direct struct { newDecompressor func() (Decompressor, error) keepAlive bool logf logger.Logf + discoPubKey tailcfg.DiscoKey mu sync.Mutex // mutex guards the following fields serverKey wgcfg.Key @@ -104,6 +105,7 @@ type Options struct { AuthKey string // optional node auth key for auto registration TimeNow func() time.Time // time.Now implementation used by Client Hostinfo *tailcfg.Hostinfo // non-nil passes ownership, nil means to use default using os.Hostname, etc + DiscoPublicKey tailcfg.DiscoKey NewDecompressor func() (Decompressor, error) KeepAlive bool Logf logger.Logf @@ -153,6 +155,7 @@ func NewDirect(opts Options) (*Direct, error) { keepAlive: opts.KeepAlive, persist: opts.Persist, authKey: opts.AuthKey, + discoPubKey: opts.DiscoPublicKey, } if opts.Hostinfo == nil { c.SetHostinfo(NewHostinfo()) @@ -478,6 +481,7 @@ func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(*NetworkM IncludeIPv6: includeIPv6(), KeepAlive: c.keepAlive, NodeKey: tailcfg.NodeKey(persist.PrivateNodeKey.Public()), + DiscoKey: c.discoPubKey, Endpoints: ep, Stream: allowStream, Hostinfo: hostinfo, diff --git a/ipn/local.go b/ipn/local.go index 2778960b3..fdea6a35a 100644 --- a/ipn/local.go +++ b/ipn/local.go @@ -358,6 +358,10 @@ func (b *LocalBackend) Start(opts Options) error { b.updateFilter(nil) + discoPrivate := key.NewPrivate() + b.e.SetDiscoPrivateKey(discoPrivate) + discoPublic := tailcfg.DiscoKey(discoPrivate.Public()) + var err error if persist == nil { // let controlclient initialize it @@ -372,6 +376,7 @@ func (b *LocalBackend) Start(opts Options) error { KeepAlive: true, NewDecompressor: b.newDecompressor, HTTPTestClient: opts.HTTPTestClient, + DiscoPublicKey: discoPublic, }) if err != nil { return err diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index e6b363e1b..9f4668019 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -449,6 +449,7 @@ type MapRequest struct { Compress string // "zstd" or "" (no compression) KeepAlive bool // server sends keep-alives NodeKey NodeKey + DiscoKey DiscoKey Endpoints []string // caller's endpoints (IPv4 or IPv6) IncludeIPv6 bool // include IPv6 endpoints in returned Node Endpoints Stream bool // if true, multiple MapResponse objects are returned diff --git a/types/key/key.go b/types/key/key.go index cf29da9a5..5aeaa0f7c 100644 --- a/types/key/key.go +++ b/types/key/key.go @@ -6,9 +6,11 @@ package key import ( + crand "crypto/rand" "encoding/base64" "errors" "fmt" + "io" "go4.org/mem" "golang.org/x/crypto/curve25519" @@ -20,6 +22,15 @@ type Private [32]byte // Private reports whether p is the zero value. func (p Private) IsZero() bool { return p == Private{} } +// NewPrivate returns a new private key. +func NewPrivate() Private { + var p Private + if _, err := io.ReadFull(crand.Reader, p[:]); err != nil { + panic(err) + } + return p +} + // B32 returns k as the *[32]byte type that's used by the // golang.org/x/crypto packages. This allocates; it might // not be appropriate for performance-sensitive paths. diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 2c7eb7e36..5aba3bbbd 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -84,6 +84,8 @@ type Conn struct { lastEndpoints []string peerSet map[key.Public]struct{} + discoPrivate key.Private + // addrsByUDP is a map of every remote ip:port to a priority // list of endpoint addresses for a peer. // The priority list is provided by wgengine configuration. @@ -476,6 +478,14 @@ func (c *Conn) SetNetInfoCallback(fn func(*tailcfg.NetInfo)) { } } +// SetDiscoPrivateKey sets the discovery key. +func (c *Conn) SetDiscoPrivateKey(k key.Private) { + c.mu.Lock() + defer c.mu.Unlock() + c.discoPrivate = k + c.logf("magicsock: disco key set; public: %x", k.Public()) +} + // c.mu must NOT be held. func (c *Conn) setNearestDERP(derpNum int) (wantDERP bool) { c.mu.Lock() diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 154cee057..c3b8b93f5 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -830,6 +830,10 @@ func (e *userspaceEngine) SetDERPMap(dm *tailcfg.DERPMap) { e.magicConn.SetDERPMap(dm) } +func (e *userspaceEngine) SetDiscoPrivateKey(k key.Private) { + e.magicConn.SetDiscoPrivateKey(k) +} + func (e *userspaceEngine) UpdateStatus(sb *ipnstate.StatusBuilder) { st, err := e.getStatus() if err != nil { diff --git a/wgengine/watchdog.go b/wgengine/watchdog.go index 70568e504..06e07a134 100644 --- a/wgengine/watchdog.go +++ b/wgengine/watchdog.go @@ -13,6 +13,7 @@ import ( "github.com/tailscale/wireguard-go/wgcfg" "tailscale.com/ipn/ipnstate" "tailscale.com/tailcfg" + "tailscale.com/types/key" "tailscale.com/wgengine/filter" "tailscale.com/wgengine/router" "tailscale.com/wgengine/tsdns" @@ -96,6 +97,9 @@ func (e *watchdogEngine) LinkChange(isExpensive bool) { func (e *watchdogEngine) SetDERPMap(m *tailcfg.DERPMap) { e.watchdog("SetDERPMap", func() { e.wrap.SetDERPMap(m) }) } +func (e *watchdogEngine) SetDiscoPrivateKey(k key.Private) { + e.watchdog("SetDiscoPrivateKey", func() { e.wrap.SetDiscoPrivateKey(k) }) +} func (e *watchdogEngine) Close() { e.watchdog("Close", e.wrap.Close) } diff --git a/wgengine/wgengine.go b/wgengine/wgengine.go index b583af2da..723067f7b 100644 --- a/wgengine/wgengine.go +++ b/wgengine/wgengine.go @@ -11,6 +11,7 @@ import ( "github.com/tailscale/wireguard-go/wgcfg" "tailscale.com/ipn/ipnstate" "tailscale.com/tailcfg" + "tailscale.com/types/key" "tailscale.com/wgengine/filter" "tailscale.com/wgengine/router" "tailscale.com/wgengine/tsdns" @@ -56,6 +57,7 @@ type Engine interface { // // This is called whenever the tailcontrol (control plane) // sends an updated network map. + // // The returned error is ErrNoChanges if no changes were made. Reconfig(*wgcfg.Config, *router.Config) error @@ -108,6 +110,10 @@ type Engine interface { // new NetInfo summary is available. SetNetInfoCallback(NetInfoCallback) + // SetDiscoPrivateKey sets the private key used for path discovery + // messages. + SetDiscoPrivateKey(key.Private) + // UpdateStatus populates the network state using the provided // status builder. UpdateStatus(*ipnstate.StatusBuilder)