diff --git a/cmd/derper/depaware.txt b/cmd/derper/depaware.txt index aa41defe1..262c65b4f 100644 --- a/cmd/derper/depaware.txt +++ b/cmd/derper/depaware.txt @@ -61,7 +61,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa tailscale.com/types/persist from tailscale.com/ipn tailscale.com/types/preftype from tailscale.com/ipn tailscale.com/types/structs from tailscale.com/ipn+ - tailscale.com/types/tkatype from tailscale.com/types/key + tailscale.com/types/tkatype from tailscale.com/types/key+ tailscale.com/types/views from tailscale.com/ipn/ipnstate+ tailscale.com/util/cloudenv from tailscale.com/hostinfo+ W tailscale.com/util/cmpver from tailscale.com/net/tshttpproxy diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index b8cdf16c1..cfab809cb 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -84,7 +84,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep tailscale.com/types/persist from tailscale.com/ipn tailscale.com/types/preftype from tailscale.com/cmd/tailscale/cli+ tailscale.com/types/structs from tailscale.com/ipn+ - tailscale.com/types/tkatype from tailscale.com/types/key + tailscale.com/types/tkatype from tailscale.com/types/key+ tailscale.com/types/views from tailscale.com/tailcfg+ tailscale.com/util/clientmetric from tailscale.com/net/netcheck+ tailscale.com/util/cloudenv from tailscale.com/net/dnscache+ diff --git a/control/controlclient/map.go b/control/controlclient/map.go index 15a3840ba..52c532b2a 100644 --- a/control/controlclient/map.go +++ b/control/controlclient/map.go @@ -310,6 +310,9 @@ func undeltaPeers(mapRes *tailcfg.MapResponse, prev []*tailcfg.Node) { if v := ec.Capabilities; v != nil { n.Capabilities = *v } + if v := ec.KeySignature; v != nil { + n.KeySignature = v + } } } } diff --git a/control/controlclient/map_test.go b/control/controlclient/map_test.go index 51c7d02b1..e53534e0e 100644 --- a/control/controlclient/map_test.go +++ b/control/controlclient/map_test.go @@ -209,6 +209,20 @@ func TestUndeltaPeers(t *testing.T) { Key: key.NodePublicFromRaw32(mem.B(append(make([]byte, 31), 'A'))), }), }, + { + name: "change_key_signature", + prev: peers(n(1, "foo")), + mapRes: &tailcfg.MapResponse{ + PeersChangedPatch: []*tailcfg.PeerChange{{ + NodeID: 1, + KeySignature: []byte{3, 4}, + }}, + }, want: peers(&tailcfg.Node{ + ID: 1, + Name: "foo", + KeySignature: []byte{3, 4}, + }), + }, { name: "change_disco_key", prev: peers(n(1, "foo")), diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 6f4019ad0..9a6acd43a 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -7,6 +7,7 @@ package tailcfg //go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal --clonefunc import ( + "bytes" "encoding/hex" "errors" "fmt" @@ -19,6 +20,7 @@ import ( "tailscale.com/types/key" "tailscale.com/types/opt" "tailscale.com/types/structs" + "tailscale.com/types/tkatype" "tailscale.com/util/dnsname" ) @@ -74,7 +76,8 @@ type CapabilityVersion int // 37: 2022-08-09: added Debug.{SetForceBackgroundSTUN,SetRandomizeClientPort}; Debug are sticky // 38: 2022-08-11: added PingRequest.URLIsNoise // 39: 2022-08-15: clients can talk Noise over arbitrary HTTPS port -const CurrentCapabilityVersion CapabilityVersion = 39 +// 40: 2022-08-22: added Node.KeySignature, PeersChangedPatch.KeySignature +const CurrentCapabilityVersion CapabilityVersion = 40 type StableID string @@ -172,16 +175,17 @@ type Node struct { // Sharer, if non-zero, is the user who shared this node, if different than User. Sharer UserID `json:",omitempty"` - Key key.NodePublic - KeyExpiry time.Time - Machine key.MachinePublic - DiscoKey key.DiscoPublic - Addresses []netip.Prefix // IP addresses of this Node directly - AllowedIPs []netip.Prefix // range of IP addresses to route to this node - Endpoints []string `json:",omitempty"` // IP+port (public via STUN, and local LANs) - DERP string `json:",omitempty"` // DERP-in-IP:port ("127.3.3.40:N") endpoint - Hostinfo HostinfoView - Created time.Time + Key key.NodePublic + KeyExpiry time.Time + KeySignature tkatype.MarshaledSignature `json:",omitempty"` + Machine key.MachinePublic + DiscoKey key.DiscoPublic + Addresses []netip.Prefix // IP addresses of this Node directly + AllowedIPs []netip.Prefix // range of IP addresses to route to this node + Endpoints []string `json:",omitempty"` // IP+port (public via STUN, and local LANs) + DERP string `json:",omitempty"` // DERP-in-IP:port ("127.3.3.40:N") endpoint + Hostinfo HostinfoView + Created time.Time // Tags are the list of ACL tags applied to this node. // Tags take the form of `tag:` where value starts @@ -1439,6 +1443,7 @@ func (n *Node) Equal(n2 *Node) bool { n.Sharer == n2.Sharer && n.Key == n2.Key && n.KeyExpiry.Equal(n2.KeyExpiry) && + bytes.Equal(n.KeySignature, n2.KeySignature) && n.Machine == n2.Machine && n.DiscoKey == n2.DiscoKey && eqBoolPtr(n.Online, n2.Online) && @@ -1799,6 +1804,10 @@ type PeerChange struct { // Key, if non-nil, means that the NodeID's wireguard public key changed. Key *key.NodePublic `json:",omitempty"` + // KeySignature, if non-nil, means that the signature of the wireguard + // public key has changed. + KeySignature tkatype.MarshaledSignature `json:",omitempty"` + // DiscoKey, if non-nil, means that the NodeID's discokey changed. DiscoKey *key.DiscoPublic `json:",omitempty"` diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index 5830a1f36..fac834fbe 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -14,6 +14,7 @@ import ( "tailscale.com/types/key" "tailscale.com/types/opt" "tailscale.com/types/structs" + "tailscale.com/types/tkatype" ) // Clone makes a deep copy of User. @@ -47,6 +48,7 @@ func (src *Node) Clone() *Node { } dst := new(Node) *dst = *src + dst.KeySignature = append(src.KeySignature[:0:0], src.KeySignature...) dst.Addresses = append(src.Addresses[:0:0], src.Addresses...) dst.AllowedIPs = append(src.AllowedIPs[:0:0], src.AllowedIPs...) dst.Endpoints = append(src.Endpoints[:0:0], src.Endpoints...) @@ -74,6 +76,7 @@ var _NodeCloneNeedsRegeneration = Node(struct { Sharer UserID Key key.NodePublic KeyExpiry time.Time + KeySignature tkatype.MarshaledSignature Machine key.MachinePublic DiscoKey key.DiscoPublic Addresses []netip.Prefix diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go index 36256ec02..336e238fd 100644 --- a/tailcfg/tailcfg_test.go +++ b/tailcfg/tailcfg_test.go @@ -305,7 +305,7 @@ func TestHostinfoTailscaleSSHEnabled(t *testing.T) { func TestNodeEqual(t *testing.T) { nodeHandles := []string{ "ID", "StableID", "Name", "User", "Sharer", - "Key", "KeyExpiry", "Machine", "DiscoKey", + "Key", "KeyExpiry", "KeySignature", "Machine", "DiscoKey", "Addresses", "AllowedIPs", "Endpoints", "DERP", "Hostinfo", "Created", "Tags", "PrimaryRoutes", "LastSeen", "Online", "KeepAlive", "MachineAuthorized", diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index bf5a387d7..a6730102f 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -12,10 +12,12 @@ import ( "net/netip" "time" + "go4.org/mem" "tailscale.com/types/dnstype" "tailscale.com/types/key" "tailscale.com/types/opt" "tailscale.com/types/structs" + "tailscale.com/types/tkatype" "tailscale.com/types/views" ) @@ -137,6 +139,7 @@ func (v NodeView) User() UserID { return v.ж.User } func (v NodeView) Sharer() UserID { return v.ж.Sharer } func (v NodeView) Key() key.NodePublic { return v.ж.Key } func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry } +func (v NodeView) KeySignature() mem.RO { return mem.B(v.ж.KeySignature) } func (v NodeView) Machine() key.MachinePublic { return v.ж.Machine } func (v NodeView) DiscoKey() key.DiscoPublic { return v.ж.DiscoKey } func (v NodeView) Addresses() views.IPPrefixSlice { return views.IPPrefixSliceOf(v.ж.Addresses) } @@ -181,6 +184,7 @@ var _NodeViewNeedsRegeneration = Node(struct { Sharer UserID Key key.NodePublic KeyExpiry time.Time + KeySignature tkatype.MarshaledSignature Machine key.MachinePublic DiscoKey key.DiscoPublic Addresses []netip.Prefix diff --git a/util/deephash/deephash_test.go b/util/deephash/deephash_test.go index b89ef6971..31d7a8deb 100644 --- a/util/deephash/deephash_test.go +++ b/util/deephash/deephash_test.go @@ -559,7 +559,7 @@ func TestGetTypeHasher(t *testing.T) { { name: "tailcfg.Node", val: &tailcfg.Node{}, - out: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + u64(uint64(time.Time{}.Unix())) + u32(0) + u32(0) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + u64(uint64(time.Time{}.Unix())) + u32(0) + u32(0) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + out: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + u64(uint64(time.Time{}.Unix())) + u64(0) + u32(0) + u32(0) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + u64(uint64(time.Time{}.Unix())) + u32(0) + u32(0) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", }, } for _, tt := range tests {