ipn,wgengine/magicsock: allow setting static node endpoints via tailscaled configfile (#12882)
wgengine/magicsock,ipn: allow setting static node endpoints via tailscaled config file. Adds a new StaticEndpoints field to tailscaled config that can be used to statically configure the endpoints that the node advertizes. This field will replace TS_DEBUG_PRETENDPOINTS env var that can be used to achieve the same. Additionally adds some functionality that ensures that endpoints are updated when configfile is reloaded. Also, refactor configuring/reconfiguring components to use the same functionality when configfile is parsed the first time or subsequent times (after reload). Previously a configfile reload did not result in resetting of prefs. Now it does- but does not yet tell the relevant components to consume the new prefs. This is to be done in a follow-up. Updates tailscale/tailscale#12578 Signed-off-by: Irbe Krumina <irbe@tailscale.com>
This commit is contained in:
parent
9904421853
commit
57856fc0d5
|
@ -42,6 +42,10 @@ type ConfigVAlpha struct {
|
|||
AutoUpdate *AutoUpdatePrefs `json:",omitempty"`
|
||||
ServeConfigTemp *ServeConfig `json:",omitempty"` // TODO(bradfitz,maisem): make separate stable type for this
|
||||
|
||||
// StaticEndpoints are additional, user-defined endpoints that this node
|
||||
// should advertise amongst its wireguard endpoints.
|
||||
StaticEndpoints []netip.AddrPort `json:",omitempty"`
|
||||
|
||||
// TODO(bradfitz,maisem): future something like:
|
||||
// Profile map[string]*Config // keyed by alice@gmail.com, corp.com (TailnetSID)
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sort"
|
||||
|
@ -391,18 +392,6 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
|||
sds.SetDialer(dialer.SystemDial)
|
||||
}
|
||||
|
||||
if sys.InitialConfig != nil {
|
||||
p := pm.CurrentPrefs().AsStruct()
|
||||
mp, err := sys.InitialConfig.Parsed.ToPrefs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.ApplyEdits(&mp)
|
||||
if err := pm.SetPrefs(p.View(), ipn.NetworkProfile{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
envknob.LogCurrent(logf)
|
||||
osshare.SetFileSharingEnabled(false, logf)
|
||||
|
||||
|
@ -417,7 +406,6 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
|||
statsLogf: logger.LogOnChange(logf, 5*time.Minute, clock.Now),
|
||||
sys: sys,
|
||||
health: sys.HealthTracker(),
|
||||
conf: sys.InitialConfig,
|
||||
e: e,
|
||||
dialer: dialer,
|
||||
store: store,
|
||||
|
@ -434,6 +422,12 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
|||
}
|
||||
mConn.SetNetInfoCallback(b.setNetInfo)
|
||||
|
||||
if sys.InitialConfig != nil {
|
||||
if err := b.setConfigLocked(sys.InitialConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
netMon := sys.NetMon.Get()
|
||||
b.sockstatLogger, err = sockstatlog.NewLogger(logpolicy.LogsDir(logf), logf, logID, netMon, sys.HealthTracker())
|
||||
if err != nil {
|
||||
|
@ -616,11 +610,50 @@ func (b *LocalBackend) ReloadConfig() (ok bool, err error) {
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
b.conf = conf
|
||||
// TODO(bradfitz): apply things
|
||||
if err := b.setConfigLocked(conf); err != nil {
|
||||
return false, fmt.Errorf("error setting config: %w", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (b *LocalBackend) setConfigLocked(conf *conffile.Config) error {
|
||||
|
||||
// TODO(irbekrm): notify the relevant components to consume any prefs
|
||||
// updates. Currently only initial configfile settings are applied
|
||||
// immediately.
|
||||
p := b.pm.CurrentPrefs().AsStruct()
|
||||
mp, err := conf.Parsed.ToPrefs()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing config to prefs: %w", err)
|
||||
}
|
||||
p.ApplyEdits(&mp)
|
||||
if err := b.pm.SetPrefs(p.View(), ipn.NetworkProfile{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
b.conf = conf
|
||||
}()
|
||||
|
||||
if conf.Parsed.StaticEndpoints == nil && (b.conf == nil || b.conf.Parsed.StaticEndpoints == nil) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure that magicsock conn has the up to date static wireguard
|
||||
// endpoints. Setting the endpoints here triggers an asynchronous update
|
||||
// of the node's advertised endpoints.
|
||||
if b.conf == nil && len(conf.Parsed.StaticEndpoints) != 0 || !reflect.DeepEqual(conf.Parsed.StaticEndpoints, b.conf.Parsed.StaticEndpoints) {
|
||||
ms, ok := b.sys.MagicSock.GetOK()
|
||||
if !ok {
|
||||
b.logf("[unexpected] ReloadConfig: MagicSock not set")
|
||||
} else {
|
||||
ms.SetStaticEndpoints(views.SliceOf(conf.Parsed.StaticEndpoints))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var assumeNetworkUpdateForTest = envknob.RegisterBool("TS_ASSUME_NETWORK_UP_FOR_TEST")
|
||||
|
||||
// pauseOrResumeControlClientLocked pauses b.cc if there is no network available
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -311,6 +312,12 @@ type Conn struct {
|
|||
// lastEPERMRebind tracks the last time a rebind was performed
|
||||
// after experiencing a syscall.EPERM.
|
||||
lastEPERMRebind syncs.AtomicValue[time.Time]
|
||||
|
||||
// staticEndpoints are user set endpoints that this node should
|
||||
// advertise amongst its wireguard endpoints. It is user's
|
||||
// responsibility to ensure that traffic from these endpoints is routed
|
||||
// to the node.
|
||||
staticEndpoints views.Slice[netip.AddrPort]
|
||||
}
|
||||
|
||||
// SetDebugLoggingEnabled controls whether spammy debug logging is enabled.
|
||||
|
@ -636,6 +643,22 @@ func (c *Conn) setEndpoints(endpoints []tailcfg.Endpoint) (changed bool) {
|
|||
return true
|
||||
}
|
||||
|
||||
// SetStaticEndpoints sets static endpoints to the provided value and triggers
|
||||
// an asynchronous update of the endpoints that this node advertises.
|
||||
// Static endpoints are endpoints explicitly configured by user.
|
||||
func (c *Conn) SetStaticEndpoints(ep views.Slice[netip.AddrPort]) {
|
||||
c.mu.Lock()
|
||||
if reflect.DeepEqual(c.staticEndpoints.AsSlice(), ep.AsSlice()) {
|
||||
return
|
||||
}
|
||||
c.staticEndpoints = ep
|
||||
c.mu.Unlock()
|
||||
// Technically this is not a reSTUNning, but ReSTUN does what we need at
|
||||
// this point- calls updateEndpoints or queues an update if there is
|
||||
// already an in-progress update.
|
||||
c.ReSTUN("static-endpoint-change")
|
||||
}
|
||||
|
||||
// setNetInfoHavePortMap updates NetInfo.HavePortMap to true.
|
||||
func (c *Conn) setNetInfoHavePortMap() {
|
||||
c.mu.Lock()
|
||||
|
@ -845,8 +868,10 @@ func (c *Conn) DiscoPublicKey() key.DiscoPublic {
|
|||
return c.discoPublic
|
||||
}
|
||||
|
||||
// determineEndpoints returns the machine's endpoint addresses. It
|
||||
// does a STUN lookup (via netcheck) to determine its public address.
|
||||
// determineEndpoints returns the machine's endpoint addresses. It does a STUN
|
||||
// lookup (via netcheck) to determine its public address. Additionally any
|
||||
// static enpoints provided by user are always added to the returned endpoints
|
||||
// without validating if the node can be reached via those endpoints.
|
||||
//
|
||||
// c.mu must NOT be held.
|
||||
func (c *Conn) determineEndpoints(ctx context.Context) ([]tailcfg.Endpoint, error) {
|
||||
|
@ -943,6 +968,10 @@ func (c *Conn) determineEndpoints(ctx context.Context) ([]tailcfg.Endpoint, erro
|
|||
// re-run.
|
||||
eps = c.endpointTracker.update(time.Now(), eps)
|
||||
|
||||
for i := range c.staticEndpoints.Len() {
|
||||
addAddr(c.staticEndpoints.At(i), tailcfg.EndpointExplicitConf)
|
||||
}
|
||||
|
||||
if localAddr := c.pconn4.LocalAddr(); localAddr.IP.IsUnspecified() {
|
||||
ips, loopback, err := netmon.LocalAddresses()
|
||||
if err != nil {
|
||||
|
@ -2359,6 +2388,8 @@ func (c *Conn) onPortMapChanged() { c.ReSTUN("portmap-changed") }
|
|||
|
||||
// ReSTUN triggers an address discovery.
|
||||
// The provided why string is for debug logging only.
|
||||
// If Conn.staticEndpoints have been updated, calling ReSTUN will also result in
|
||||
// the new endpoints being advertised.
|
||||
func (c *Conn) ReSTUN(why string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
|
Loading…
Reference in New Issue