mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
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"`
|
AutoUpdate *AutoUpdatePrefs `json:",omitempty"`
|
||||||
ServeConfigTemp *ServeConfig `json:",omitempty"` // TODO(bradfitz,maisem): make separate stable type for this
|
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:
|
// TODO(bradfitz,maisem): future something like:
|
||||||
// Profile map[string]*Config // keyed by alice@gmail.com, corp.com (TailnetSID)
|
// Profile map[string]*Config // keyed by alice@gmail.com, corp.com (TailnetSID)
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
@ -391,18 +392,6 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
|||||||
sds.SetDialer(dialer.SystemDial)
|
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)
|
envknob.LogCurrent(logf)
|
||||||
osshare.SetFileSharingEnabled(false, 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),
|
statsLogf: logger.LogOnChange(logf, 5*time.Minute, clock.Now),
|
||||||
sys: sys,
|
sys: sys,
|
||||||
health: sys.HealthTracker(),
|
health: sys.HealthTracker(),
|
||||||
conf: sys.InitialConfig,
|
|
||||||
e: e,
|
e: e,
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
store: store,
|
store: store,
|
||||||
@ -434,6 +422,12 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
|||||||
}
|
}
|
||||||
mConn.SetNetInfoCallback(b.setNetInfo)
|
mConn.SetNetInfoCallback(b.setNetInfo)
|
||||||
|
|
||||||
|
if sys.InitialConfig != nil {
|
||||||
|
if err := b.setConfigLocked(sys.InitialConfig); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
netMon := sys.NetMon.Get()
|
netMon := sys.NetMon.Get()
|
||||||
b.sockstatLogger, err = sockstatlog.NewLogger(logpolicy.LogsDir(logf), logf, logID, netMon, sys.HealthTracker())
|
b.sockstatLogger, err = sockstatlog.NewLogger(logpolicy.LogsDir(logf), logf, logID, netMon, sys.HealthTracker())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -616,11 +610,50 @@ func (b *LocalBackend) ReloadConfig() (ok bool, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
b.conf = conf
|
if err := b.setConfigLocked(conf); err != nil {
|
||||||
// TODO(bradfitz): apply things
|
return false, fmt.Errorf("error setting config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return true, nil
|
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")
|
var assumeNetworkUpdateForTest = envknob.RegisterBool("TS_ASSUME_NETWORK_UP_FOR_TEST")
|
||||||
|
|
||||||
// pauseOrResumeControlClientLocked pauses b.cc if there is no network available
|
// pauseOrResumeControlClientLocked pauses b.cc if there is no network available
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -311,6 +312,12 @@ type Conn struct {
|
|||||||
// lastEPERMRebind tracks the last time a rebind was performed
|
// lastEPERMRebind tracks the last time a rebind was performed
|
||||||
// after experiencing a syscall.EPERM.
|
// after experiencing a syscall.EPERM.
|
||||||
lastEPERMRebind syncs.AtomicValue[time.Time]
|
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.
|
// SetDebugLoggingEnabled controls whether spammy debug logging is enabled.
|
||||||
@ -636,6 +643,22 @@ func (c *Conn) setEndpoints(endpoints []tailcfg.Endpoint) (changed bool) {
|
|||||||
return true
|
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.
|
// setNetInfoHavePortMap updates NetInfo.HavePortMap to true.
|
||||||
func (c *Conn) setNetInfoHavePortMap() {
|
func (c *Conn) setNetInfoHavePortMap() {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
@ -845,8 +868,10 @@ func (c *Conn) DiscoPublicKey() key.DiscoPublic {
|
|||||||
return c.discoPublic
|
return c.discoPublic
|
||||||
}
|
}
|
||||||
|
|
||||||
// determineEndpoints returns the machine's endpoint addresses. It
|
// determineEndpoints returns the machine's endpoint addresses. It does a STUN
|
||||||
// does a STUN lookup (via netcheck) to determine its public address.
|
// 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.
|
// c.mu must NOT be held.
|
||||||
func (c *Conn) determineEndpoints(ctx context.Context) ([]tailcfg.Endpoint, error) {
|
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.
|
// re-run.
|
||||||
eps = c.endpointTracker.update(time.Now(), eps)
|
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() {
|
if localAddr := c.pconn4.LocalAddr(); localAddr.IP.IsUnspecified() {
|
||||||
ips, loopback, err := netmon.LocalAddresses()
|
ips, loopback, err := netmon.LocalAddresses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -2359,6 +2388,8 @@ func (c *Conn) onPortMapChanged() { c.ReSTUN("portmap-changed") }
|
|||||||
|
|
||||||
// ReSTUN triggers an address discovery.
|
// ReSTUN triggers an address discovery.
|
||||||
// The provided why string is for debug logging only.
|
// 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) {
|
func (c *Conn) ReSTUN(why string) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
Loading…
Reference in New Issue
Block a user