mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 21:27:31 +00:00
ipn: program exit node into the data plane according to user pref.
Part of #1153, #1154. Fixes #1224. Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:

committed by
Dave Anderson

parent
fb6b0e247c
commit
b9c2231fdf
@@ -306,8 +306,10 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
|
||||
prefsChanged = true
|
||||
}
|
||||
if st.NetMap != nil {
|
||||
if b.keepOneExitNodeLocked(st.NetMap) {
|
||||
prefsChanged = true
|
||||
}
|
||||
b.setNetMapLocked(st.NetMap)
|
||||
|
||||
}
|
||||
if st.URL != "" {
|
||||
b.authURL = st.URL
|
||||
@@ -365,6 +367,57 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
|
||||
b.authReconfig()
|
||||
}
|
||||
|
||||
// keepOneExitNodeLocked edits nm to retain only the default
|
||||
// routes provided by the exit node specified in b.prefs. It returns
|
||||
// whether prefs was mutated as part of the process, due to an exit
|
||||
// node IP being converted into a node ID.
|
||||
func (b *LocalBackend) keepOneExitNodeLocked(nm *controlclient.NetworkMap) (prefsChanged bool) {
|
||||
if b.prefs.ExitNodeID == "" && b.prefs.ExitNodeIP.IsZero() {
|
||||
return false
|
||||
}
|
||||
|
||||
// If we have a desired IP on file, try to find the corresponding
|
||||
// node.
|
||||
if !b.prefs.ExitNodeIP.IsZero() {
|
||||
// IP takes precedence over ID, so if both are set, clear ID.
|
||||
if b.prefs.ExitNodeID != "" {
|
||||
b.prefs.ExitNodeID = ""
|
||||
prefsChanged = true
|
||||
}
|
||||
|
||||
peerLoop:
|
||||
for _, peer := range nm.Peers {
|
||||
for _, addr := range peer.Addresses {
|
||||
if !addr.IsSingleIP() || addr.IP != b.prefs.ExitNodeIP {
|
||||
continue
|
||||
}
|
||||
// Found the node being referenced, upgrade prefs to
|
||||
// reference it directly for next time.
|
||||
b.prefs.ExitNodeID = peer.StableID
|
||||
b.prefs.ExitNodeIP = netaddr.IP{}
|
||||
prefsChanged = true
|
||||
break peerLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we have a node ID if the requested node is in
|
||||
// the netmap. If not, the ID will be empty, and we'll strip out
|
||||
// all default routes.
|
||||
for _, peer := range nm.Peers {
|
||||
out := peer.AllowedIPs[:0]
|
||||
for _, allowedIP := range peer.AllowedIPs {
|
||||
if allowedIP.Bits == 0 && peer.StableID != b.prefs.ExitNodeID {
|
||||
continue
|
||||
}
|
||||
out = append(out, allowedIP)
|
||||
}
|
||||
peer.AllowedIPs = out
|
||||
}
|
||||
|
||||
return prefsChanged
|
||||
}
|
||||
|
||||
// setWgengineStatus is the callback by the wireguard engine whenever it posts a new status.
|
||||
// This updates the endpoints both in the backend and in the control client.
|
||||
func (b *LocalBackend) setWgengineStatus(s *wgengine.Status, err error) {
|
||||
@@ -1203,8 +1256,6 @@ func (b *LocalBackend) authReconfig() {
|
||||
|
||||
var flags controlclient.WGConfigFlags
|
||||
if uc.RouteAll {
|
||||
flags |= controlclient.AllowDefaultRoute
|
||||
// TODO(apenwarr): Make subnet routes a different pref?
|
||||
flags |= controlclient.AllowSubnetRoutes
|
||||
}
|
||||
if uc.AllowSingleHosts {
|
||||
@@ -1256,6 +1307,11 @@ func magicDNSRootDomains(nm *controlclient.NetworkMap) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
ipv4Default = netaddr.MustParseIPPrefix("0.0.0.0/0")
|
||||
ipv6Default = netaddr.MustParseIPPrefix("::/0")
|
||||
)
|
||||
|
||||
// routerConfig produces a router.Config from a wireguard config and IPN prefs.
|
||||
func routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs) *router.Config {
|
||||
rs := &router.Config{
|
||||
@@ -1269,6 +1325,32 @@ func routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs) *router.Config {
|
||||
rs.Routes = append(rs.Routes, unmapIPPrefixes(peer.AllowedIPs)...)
|
||||
}
|
||||
|
||||
// Sanity check: we expect the control server to program both a v4
|
||||
// and a v6 default route, if default routing is on. Fill in
|
||||
// blackhole routes appropriately if we're missing some. This is
|
||||
// likely to break some functionality, but if the user expressed a
|
||||
// preference for routing remotely, we want to avoid leaking
|
||||
// traffic at the expense of functionality.
|
||||
if prefs.ExitNodeID != "" || !prefs.ExitNodeIP.IsZero() {
|
||||
var default4, default6 bool
|
||||
for _, route := range rs.Routes {
|
||||
if route == ipv4Default {
|
||||
default4 = true
|
||||
} else if route == ipv6Default {
|
||||
default6 = true
|
||||
}
|
||||
if default4 && default6 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !default4 {
|
||||
rs.Routes = append(rs.Routes, ipv4Default)
|
||||
}
|
||||
if !default6 {
|
||||
rs.Routes = append(rs.Routes, ipv6Default)
|
||||
}
|
||||
}
|
||||
|
||||
rs.Routes = append(rs.Routes, netaddr.IPPrefix{
|
||||
IP: tsaddr.TailscaleServiceIP(),
|
||||
Bits: 32,
|
||||
|
Reference in New Issue
Block a user