From 6064b6ff479909a3f5a799d72d9c2062ad856aa3 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 5 Feb 2021 12:44:43 -0800 Subject: [PATCH] wgengine/wgcfg/nmcfg: split control/controlclient/netmap.go into own package It couldn't move to ipnlocal due to test dependency cycles. Updates #1278 Signed-off-by: Brad Fitzpatrick --- cmd/tailscale/depaware.txt | 23 +---- cmd/tailscaled/depaware.txt | 3 +- control/controlclient/netmap.go | 117 ------------------------- ipn/ipnlocal/local.go | 3 +- wgengine/magicsock/magicsock.go | 5 +- wgengine/magicsock/magicsock_test.go | 3 +- wgengine/wgcfg/config.go | 5 ++ wgengine/wgcfg/nmcfg/nmcfg.go | 126 +++++++++++++++++++++++++++ 8 files changed, 144 insertions(+), 141 deletions(-) create mode 100644 wgengine/wgcfg/nmcfg/nmcfg.go diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index 3581fd047..2113e09ed 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -7,16 +7,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep L github.com/mdlayher/sdnotify from tailscale.com/util/systemd github.com/peterbourgon/ff/v2 from github.com/peterbourgon/ff/v2/ffcli github.com/peterbourgon/ff/v2/ffcli from tailscale.com/cmd/tailscale/cli - 💣 github.com/tailscale/wireguard-go/conn from github.com/tailscale/wireguard-go/device - 💣 github.com/tailscale/wireguard-go/device from tailscale.com/wgengine/wgcfg - 💣 github.com/tailscale/wireguard-go/ipc from github.com/tailscale/wireguard-go/device - W 💣 github.com/tailscale/wireguard-go/ipc/winpipe from github.com/tailscale/wireguard-go/ipc - github.com/tailscale/wireguard-go/ratelimiter from github.com/tailscale/wireguard-go/device - github.com/tailscale/wireguard-go/replay from github.com/tailscale/wireguard-go/device - github.com/tailscale/wireguard-go/rwcancel from github.com/tailscale/wireguard-go/device+ - github.com/tailscale/wireguard-go/tai64n from github.com/tailscale/wireguard-go/device - 💣 github.com/tailscale/wireguard-go/tun from github.com/tailscale/wireguard-go/device - W 💣 github.com/tailscale/wireguard-go/tun/wintun from github.com/tailscale/wireguard-go/tun github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli 💣 go4.org/intern from inet.af/netaddr @@ -45,7 +35,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep tailscale.com/net/packet from tailscale.com/wgengine/filter tailscale.com/net/stun from tailscale.com/net/netcheck tailscale.com/net/tlsdial from tailscale.com/control/controlclient+ - tailscale.com/net/tsaddr from tailscale.com/net/interfaces+ + tailscale.com/net/tsaddr from tailscale.com/net/interfaces 💣 tailscale.com/net/tshttpproxy from tailscale.com/control/controlclient+ tailscale.com/paths from tailscale.com/cmd/tailscale/cli tailscale.com/safesocket from tailscale.com/cmd/tailscale/cli @@ -67,9 +57,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep tailscale.com/version from tailscale.com/cmd/tailscale/cli+ tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+ tailscale.com/wgengine/filter from tailscale.com/control/controlclient - tailscale.com/wgengine/wgcfg from tailscale.com/control/controlclient golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box - golang.org/x/crypto/blake2s from github.com/tailscale/wireguard-go/device golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305 golang.org/x/crypto/chacha20poly1305 from crypto/tls+ golang.org/x/crypto/cryptobyte from crypto/ecdsa+ @@ -78,17 +66,14 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep golang.org/x/crypto/hkdf from crypto/tls golang.org/x/crypto/nacl/box from tailscale.com/control/controlclient+ golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box - golang.org/x/crypto/poly1305 from github.com/tailscale/wireguard-go/device+ + golang.org/x/crypto/poly1305 from golang.org/x/crypto/chacha20poly1305+ golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+ - golang.org/x/net/bpf from golang.org/x/net/ipv4+ golang.org/x/net/context/ctxhttp from golang.org/x/oauth2/internal golang.org/x/net/dns/dnsmessage from net golang.org/x/net/http/httpguts from net/http golang.org/x/net/http/httpproxy from net/http golang.org/x/net/http2/hpack from net/http golang.org/x/net/idna from golang.org/x/net/http/httpguts+ - golang.org/x/net/ipv4 from github.com/tailscale/wireguard-go/device - golang.org/x/net/ipv6 from github.com/tailscale/wireguard-go/device+ golang.org/x/net/proxy from tailscale.com/net/netns D golang.org/x/net/route from net golang.org/x/oauth2 from tailscale.com/control/controlclient+ @@ -96,7 +81,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep golang.org/x/sync/errgroup from tailscale.com/derp golang.org/x/sync/singleflight from tailscale.com/net/dnscache golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+ - LD golang.org/x/sys/unix from github.com/tailscale/wireguard-go/conn+ + LD golang.org/x/sys/unix from tailscale.com/net/netns+ W golang.org/x/sys/windows from github.com/apenwarr/fixconsole+ W golang.org/x/sys/windows/registry from golang.zx2c4.com/wireguard/windows/tunnel/winipcfg golang.org/x/text/secure/bidirule from golang.org/x/net/idna @@ -157,7 +142,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep math from compress/flate+ math/big from crypto/dsa+ math/bits from compress/flate+ - math/rand from github.com/tailscale/wireguard-go/device+ + math/rand from math/big+ mime from golang.org/x/oauth2/internal+ mime/multipart from net/http mime/quotedprintable from mime/multipart diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index cf4e13c10..e65e8bfa2 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -129,7 +129,8 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/wgengine/router/dns from tailscale.com/ipn/ipnlocal+ tailscale.com/wgengine/tsdns from tailscale.com/ipn/ipnlocal+ tailscale.com/wgengine/tstun from tailscale.com/wgengine+ - tailscale.com/wgengine/wgcfg from tailscale.com/control/controlclient+ + tailscale.com/wgengine/wgcfg from tailscale.com/ipn/ipnlocal+ + tailscale.com/wgengine/wgcfg/nmcfg from tailscale.com/ipn/ipnlocal tailscale.com/wgengine/wglog from tailscale.com/wgengine W 💣 tailscale.com/wgengine/winnet from tailscale.com/wgengine/router golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box diff --git a/control/controlclient/netmap.go b/control/controlclient/netmap.go index 15548cb2c..6589bb369 100644 --- a/control/controlclient/netmap.go +++ b/control/controlclient/netmap.go @@ -7,19 +7,14 @@ package controlclient import ( "encoding/json" "fmt" - "net" "reflect" - "strconv" "strings" "time" "inet.af/netaddr" - "tailscale.com/net/tsaddr" "tailscale.com/tailcfg" - "tailscale.com/types/logger" "tailscale.com/types/wgkey" "tailscale.com/wgengine/filter" - "tailscale.com/wgengine/wgcfg" ) type NetworkMap struct { @@ -252,118 +247,6 @@ const ( AllowSubnetRoutes ) -// EndpointDiscoSuffix is appended to the hex representation of a peer's discovery key -// and is then the sole wireguard endpoint for peers with a non-zero discovery key. -// This form is then recognize by magicsock's CreateEndpoint. -const EndpointDiscoSuffix = ".disco.tailscale:12345" - -// WGCfg returns the NetworkMaps's Wireguard configuration. -func (nm *NetworkMap) WGCfg(logf logger.Logf, flags WGConfigFlags) (*wgcfg.Config, error) { - cfg := &wgcfg.Config{ - Name: "tailscale", - PrivateKey: wgcfg.PrivateKey(nm.PrivateKey), - Addresses: nm.Addresses, - ListenPort: nm.LocalPort, - Peers: make([]wgcfg.Peer, 0, len(nm.Peers)), - } - - for _, peer := range nm.Peers { - if Debug.OnlyDisco && peer.DiscoKey.IsZero() { - continue - } - cfg.Peers = append(cfg.Peers, wgcfg.Peer{ - PublicKey: wgcfg.Key(peer.Key), - }) - cpeer := &cfg.Peers[len(cfg.Peers)-1] - if peer.KeepAlive { - cpeer.PersistentKeepalive = 25 // seconds - } - - if !peer.DiscoKey.IsZero() { - if err := appendEndpoint(cpeer, fmt.Sprintf("%x%s", peer.DiscoKey[:], EndpointDiscoSuffix)); err != nil { - return nil, err - } - cpeer.Endpoints = fmt.Sprintf("%x.disco.tailscale:12345", peer.DiscoKey[:]) - } else { - if err := appendEndpoint(cpeer, peer.DERP); err != nil { - return nil, err - } - for _, ep := range peer.Endpoints { - if err := appendEndpoint(cpeer, ep); err != nil { - return nil, err - } - } - } - - for _, allowedIP := range peer.AllowedIPs { - if allowedIP.IsSingleIP() && tsaddr.IsTailscaleIP(allowedIP.IP) && (flags&AllowSingleHosts) == 0 { - logf("[v1] wgcfg: skipping node IP %v from %q (%v)", - allowedIP.IP, nodeDebugName(peer), peer.Key.ShortString()) - continue - } else if cidrIsSubnet(peer, allowedIP) { - if (flags & AllowSubnetRoutes) == 0 { - logf("[v1] wgcfg: not accepting subnet route %v from %q (%v)", - allowedIP, nodeDebugName(peer), peer.Key.ShortString()) - continue - } - } - cpeer.AllowedIPs = append(cpeer.AllowedIPs, allowedIP) - } - } - - return cfg, nil -} - -func nodeDebugName(n *tailcfg.Node) string { - name := n.Name - if name == "" { - name = n.Hostinfo.Hostname - } - if i := strings.Index(name, "."); i != -1 { - name = name[:i] - } - if name == "" && len(n.Addresses) != 0 { - return n.Addresses[0].String() - } - return name -} - -// cidrIsSubnet reports whether cidr is a non-default-route subnet -// exported by node that is not one of its own self addresses. -func cidrIsSubnet(node *tailcfg.Node, cidr netaddr.IPPrefix) bool { - if cidr.Bits == 0 { - return false - } - if !cidr.IsSingleIP() { - return true - } - for _, selfCIDR := range node.Addresses { - if cidr == selfCIDR { - return false - } - } - return true -} - -func appendEndpoint(peer *wgcfg.Peer, epStr string) error { - if epStr == "" { - return nil - } - _, port, err := net.SplitHostPort(epStr) - if err != nil { - return fmt.Errorf("malformed endpoint %q for peer %v", epStr, peer.PublicKey.ShortString()) - } - _, err = strconv.ParseUint(port, 10, 16) - if err != nil { - return fmt.Errorf("invalid port in endpoint %q for peer %v", epStr, peer.PublicKey.ShortString()) - } - if peer.Endpoints != "" { - peer.Endpoints += "," - } - peer.Endpoints += epStr - return nil -} - // eqStringsIgnoreNil reports whether a and b have the same length and // contents, but ignore whether a or b are nil. func eqStringsIgnoreNil(a, b []string) bool { diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index fd4bac434..1bd974ad3 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -38,6 +38,7 @@ import ( "tailscale.com/wgengine/router/dns" "tailscale.com/wgengine/tsdns" "tailscale.com/wgengine/wgcfg" + "tailscale.com/wgengine/wgcfg/nmcfg" ) var controlDebugFlags = getControlDebugFlags() @@ -1268,7 +1269,7 @@ func (b *LocalBackend) authReconfig() { } } - cfg, err := nm.WGCfg(b.logf, flags) + cfg, err := nmcfg.WGCfg(nm, b.logf, flags) if err != nil { b.logf("wgcfg: %v", err) return diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 5e3120453..3147167bb 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -51,6 +51,7 @@ import ( "tailscale.com/types/nettype" "tailscale.com/types/wgkey" "tailscale.com/version" + "tailscale.com/wgengine/wgcfg" ) // Various debugging and experimental tweakables, set by environment @@ -2643,11 +2644,11 @@ func (c *Conn) CreateEndpoint(pubKey [32]byte, addrs string) (conn.Endpoint, err pk := key.Public(pubKey) c.logf("magicsock: CreateEndpoint: key=%s: %s", pk.ShortString(), derpStr(addrs)) - if !strings.HasSuffix(addrs, controlclient.EndpointDiscoSuffix) { + if !strings.HasSuffix(addrs, wgcfg.EndpointDiscoSuffix) { return c.createLegacyEndpointLocked(pk, addrs) } - discoHex := strings.TrimSuffix(addrs, controlclient.EndpointDiscoSuffix) + discoHex := strings.TrimSuffix(addrs, wgcfg.EndpointDiscoSuffix) discoKey, err := key.NewPublicFromHexMem(mem.S(discoHex)) if err != nil { return nil, fmt.Errorf("magicsock: invalid discokey endpoint %q for %v: %w", addrs, pk.ShortString(), err) diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go index 094e73359..90a78b9b0 100644 --- a/wgengine/magicsock/magicsock_test.go +++ b/wgengine/magicsock/magicsock_test.go @@ -46,6 +46,7 @@ import ( "tailscale.com/wgengine/filter" "tailscale.com/wgengine/tstun" "tailscale.com/wgengine/wgcfg" + "tailscale.com/wgengine/wgcfg/nmcfg" "tailscale.com/wgengine/wglog" ) @@ -293,7 +294,7 @@ func meshStacks(logf logger.Logf, ms []*magicStack) (cleanup func()) { peerSet[key.Public(peer.Key)] = struct{}{} } m.conn.UpdatePeers(peerSet) - wg, err := netmap.WGCfg(logf, controlclient.AllowSingleHosts) + wg, err := nmcfg.WGCfg(netmap, logf, controlclient.AllowSingleHosts) if err != nil { // We're too far from the *testing.T to be graceful, // blow up. Shouldn't happen anyway. diff --git a/wgengine/wgcfg/config.go b/wgengine/wgcfg/config.go index af86b36d6..2928e47d2 100644 --- a/wgengine/wgcfg/config.go +++ b/wgengine/wgcfg/config.go @@ -9,6 +9,11 @@ import ( "inet.af/netaddr" ) +// EndpointDiscoSuffix is appended to the hex representation of a peer's discovery key +// and is then the sole wireguard endpoint for peers with a non-zero discovery key. +// This form is then recognize by magicsock's CreateEndpoint. +const EndpointDiscoSuffix = ".disco.tailscale:12345" + // Config is a WireGuard configuration. // It only supports the set of things Tailscale uses. type Config struct { diff --git a/wgengine/wgcfg/nmcfg/nmcfg.go b/wgengine/wgcfg/nmcfg/nmcfg.go new file mode 100644 index 000000000..ce8408b06 --- /dev/null +++ b/wgengine/wgcfg/nmcfg/nmcfg.go @@ -0,0 +1,126 @@ +// Copyright (c) 2020 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. + +// Package nmcfg converts a controlclient.NetMap into a wgcfg config. +package nmcfg + +import ( + "fmt" + "net" + "strconv" + "strings" + + "inet.af/netaddr" + "tailscale.com/control/controlclient" + "tailscale.com/net/tsaddr" + "tailscale.com/tailcfg" + "tailscale.com/types/logger" + "tailscale.com/wgengine/wgcfg" +) + +func nodeDebugName(n *tailcfg.Node) string { + name := n.Name + if name == "" { + name = n.Hostinfo.Hostname + } + if i := strings.Index(name, "."); i != -1 { + name = name[:i] + } + if name == "" && len(n.Addresses) != 0 { + return n.Addresses[0].String() + } + return name +} + +// cidrIsSubnet reports whether cidr is a non-default-route subnet +// exported by node that is not one of its own self addresses. +func cidrIsSubnet(node *tailcfg.Node, cidr netaddr.IPPrefix) bool { + if cidr.Bits == 0 { + return false + } + if !cidr.IsSingleIP() { + return true + } + for _, selfCIDR := range node.Addresses { + if cidr == selfCIDR { + return false + } + } + return true +} + +// WGCfg returns the NetworkMaps's Wireguard configuration. +func WGCfg(nm *controlclient.NetworkMap, logf logger.Logf, flags controlclient.WGConfigFlags) (*wgcfg.Config, error) { + cfg := &wgcfg.Config{ + Name: "tailscale", + PrivateKey: wgcfg.PrivateKey(nm.PrivateKey), + Addresses: nm.Addresses, + ListenPort: nm.LocalPort, + Peers: make([]wgcfg.Peer, 0, len(nm.Peers)), + } + + for _, peer := range nm.Peers { + if controlclient.Debug.OnlyDisco && peer.DiscoKey.IsZero() { + continue + } + cfg.Peers = append(cfg.Peers, wgcfg.Peer{ + PublicKey: wgcfg.Key(peer.Key), + }) + cpeer := &cfg.Peers[len(cfg.Peers)-1] + if peer.KeepAlive { + cpeer.PersistentKeepalive = 25 // seconds + } + + if !peer.DiscoKey.IsZero() { + if err := appendEndpoint(cpeer, fmt.Sprintf("%x%s", peer.DiscoKey[:], wgcfg.EndpointDiscoSuffix)); err != nil { + return nil, err + } + cpeer.Endpoints = fmt.Sprintf("%x.disco.tailscale:12345", peer.DiscoKey[:]) + } else { + if err := appendEndpoint(cpeer, peer.DERP); err != nil { + return nil, err + } + for _, ep := range peer.Endpoints { + if err := appendEndpoint(cpeer, ep); err != nil { + return nil, err + } + } + } + for _, allowedIP := range peer.AllowedIPs { + if allowedIP.IsSingleIP() && tsaddr.IsTailscaleIP(allowedIP.IP) && (flags&controlclient.AllowSingleHosts) == 0 { + logf("[v1] wgcfg: skipping node IP %v from %q (%v)", + allowedIP.IP, nodeDebugName(peer), peer.Key.ShortString()) + continue + } else if cidrIsSubnet(peer, allowedIP) { + if (flags & controlclient.AllowSubnetRoutes) == 0 { + logf("[v1] wgcfg: not accepting subnet route %v from %q (%v)", + allowedIP, nodeDebugName(peer), peer.Key.ShortString()) + continue + } + } + cpeer.AllowedIPs = append(cpeer.AllowedIPs, allowedIP) + } + } + + return cfg, nil +} + +func appendEndpoint(peer *wgcfg.Peer, epStr string) error { + if epStr == "" { + return nil + } + _, port, err := net.SplitHostPort(epStr) + if err != nil { + return fmt.Errorf("malformed endpoint %q for peer %v", epStr, peer.PublicKey.ShortString()) + } + _, err = strconv.ParseUint(port, 10, 16) + if err != nil { + return fmt.Errorf("invalid port in endpoint %q for peer %v", epStr, peer.PublicKey.ShortString()) + } + if peer.Endpoints != "" { + peer.Endpoints += "," + } + peer.Endpoints += epStr + return nil +}