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 <bradfitz@tailscale.com>
pull/1281/head
Brad Fitzpatrick 3 years ago committed by Brad Fitzpatrick
parent 138055dd70
commit 6064b6ff47

@ -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 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 from github.com/peterbourgon/ff/v2/ffcli
github.com/peterbourgon/ff/v2/ffcli from tailscale.com/cmd/tailscale/cli 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/tcnksm/go-httpstat from tailscale.com/net/netcheck
github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli
💣 go4.org/intern from inet.af/netaddr 💣 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/packet from tailscale.com/wgengine/filter
tailscale.com/net/stun from tailscale.com/net/netcheck tailscale.com/net/stun from tailscale.com/net/netcheck
tailscale.com/net/tlsdial from tailscale.com/control/controlclient+ 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/net/tshttpproxy from tailscale.com/control/controlclient+
tailscale.com/paths from tailscale.com/cmd/tailscale/cli tailscale.com/paths from tailscale.com/cmd/tailscale/cli
tailscale.com/safesocket 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 from tailscale.com/cmd/tailscale/cli+
tailscale.com/version/distro 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/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/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/chacha20 from golang.org/x/crypto/chacha20poly1305
golang.org/x/crypto/chacha20poly1305 from crypto/tls+ golang.org/x/crypto/chacha20poly1305 from crypto/tls+
golang.org/x/crypto/cryptobyte from crypto/ecdsa+ 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/hkdf from crypto/tls
golang.org/x/crypto/nacl/box from tailscale.com/control/controlclient+ 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/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/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/context/ctxhttp from golang.org/x/oauth2/internal
golang.org/x/net/dns/dnsmessage from net golang.org/x/net/dns/dnsmessage from net
golang.org/x/net/http/httpguts from net/http golang.org/x/net/http/httpguts from net/http
golang.org/x/net/http/httpproxy 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/http2/hpack from net/http
golang.org/x/net/idna from golang.org/x/net/http/httpguts+ 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 golang.org/x/net/proxy from tailscale.com/net/netns
D golang.org/x/net/route from net D golang.org/x/net/route from net
golang.org/x/oauth2 from tailscale.com/control/controlclient+ 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/errgroup from tailscale.com/derp
golang.org/x/sync/singleflight from tailscale.com/net/dnscache golang.org/x/sync/singleflight from tailscale.com/net/dnscache
golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+ 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 from github.com/apenwarr/fixconsole+
W golang.org/x/sys/windows/registry from golang.zx2c4.com/wireguard/windows/tunnel/winipcfg 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 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 from compress/flate+
math/big from crypto/dsa+ math/big from crypto/dsa+
math/bits from compress/flate+ 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 from golang.org/x/oauth2/internal+
mime/multipart from net/http mime/multipart from net/http
mime/quotedprintable from mime/multipart mime/quotedprintable from mime/multipart

@ -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/router/dns from tailscale.com/ipn/ipnlocal+
tailscale.com/wgengine/tsdns 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/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 tailscale.com/wgengine/wglog from tailscale.com/wgengine
W 💣 tailscale.com/wgengine/winnet from tailscale.com/wgengine/router W 💣 tailscale.com/wgengine/winnet from tailscale.com/wgengine/router
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box

@ -7,19 +7,14 @@ package controlclient
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net"
"reflect" "reflect"
"strconv"
"strings" "strings"
"time" "time"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/logger"
"tailscale.com/types/wgkey" "tailscale.com/types/wgkey"
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
"tailscale.com/wgengine/wgcfg"
) )
type NetworkMap struct { type NetworkMap struct {
@ -252,118 +247,6 @@ const (
AllowSubnetRoutes 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 // eqStringsIgnoreNil reports whether a and b have the same length and
// contents, but ignore whether a or b are nil. // contents, but ignore whether a or b are nil.
func eqStringsIgnoreNil(a, b []string) bool { func eqStringsIgnoreNil(a, b []string) bool {

@ -38,6 +38,7 @@ import (
"tailscale.com/wgengine/router/dns" "tailscale.com/wgengine/router/dns"
"tailscale.com/wgengine/tsdns" "tailscale.com/wgengine/tsdns"
"tailscale.com/wgengine/wgcfg" "tailscale.com/wgengine/wgcfg"
"tailscale.com/wgengine/wgcfg/nmcfg"
) )
var controlDebugFlags = getControlDebugFlags() 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 { if err != nil {
b.logf("wgcfg: %v", err) b.logf("wgcfg: %v", err)
return return

@ -51,6 +51,7 @@ import (
"tailscale.com/types/nettype" "tailscale.com/types/nettype"
"tailscale.com/types/wgkey" "tailscale.com/types/wgkey"
"tailscale.com/version" "tailscale.com/version"
"tailscale.com/wgengine/wgcfg"
) )
// Various debugging and experimental tweakables, set by environment // 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) pk := key.Public(pubKey)
c.logf("magicsock: CreateEndpoint: key=%s: %s", pk.ShortString(), derpStr(addrs)) 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) return c.createLegacyEndpointLocked(pk, addrs)
} }
discoHex := strings.TrimSuffix(addrs, controlclient.EndpointDiscoSuffix) discoHex := strings.TrimSuffix(addrs, wgcfg.EndpointDiscoSuffix)
discoKey, err := key.NewPublicFromHexMem(mem.S(discoHex)) discoKey, err := key.NewPublicFromHexMem(mem.S(discoHex))
if err != nil { if err != nil {
return nil, fmt.Errorf("magicsock: invalid discokey endpoint %q for %v: %w", addrs, pk.ShortString(), err) return nil, fmt.Errorf("magicsock: invalid discokey endpoint %q for %v: %w", addrs, pk.ShortString(), err)

@ -46,6 +46,7 @@ import (
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
"tailscale.com/wgengine/tstun" "tailscale.com/wgengine/tstun"
"tailscale.com/wgengine/wgcfg" "tailscale.com/wgengine/wgcfg"
"tailscale.com/wgengine/wgcfg/nmcfg"
"tailscale.com/wgengine/wglog" "tailscale.com/wgengine/wglog"
) )
@ -293,7 +294,7 @@ func meshStacks(logf logger.Logf, ms []*magicStack) (cleanup func()) {
peerSet[key.Public(peer.Key)] = struct{}{} peerSet[key.Public(peer.Key)] = struct{}{}
} }
m.conn.UpdatePeers(peerSet) m.conn.UpdatePeers(peerSet)
wg, err := netmap.WGCfg(logf, controlclient.AllowSingleHosts) wg, err := nmcfg.WGCfg(netmap, logf, controlclient.AllowSingleHosts)
if err != nil { if err != nil {
// We're too far from the *testing.T to be graceful, // We're too far from the *testing.T to be graceful,
// blow up. Shouldn't happen anyway. // blow up. Shouldn't happen anyway.

@ -9,6 +9,11 @@ import (
"inet.af/netaddr" "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. // Config is a WireGuard configuration.
// It only supports the set of things Tailscale uses. // It only supports the set of things Tailscale uses.
type Config struct { type Config struct {

@ -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
}
Loading…
Cancel
Save