2020-02-05 22:16:58 +00:00
// 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.
2021-02-04 21:12:42 +00:00
package ipnlocal
2020-02-05 22:16:58 +00:00
import (
2020-03-14 03:53:58 +00:00
"context"
2022-11-02 03:37:13 +00:00
"encoding/base64"
2022-11-07 23:32:53 +00:00
"encoding/json"
2020-02-03 18:35:52 +00:00
"errors"
2020-02-05 22:16:58 +00:00
"fmt"
2021-03-30 20:56:00 +01:00
"io"
2021-03-26 20:44:55 +00:00
"net"
2021-04-07 16:39:08 +01:00
"net/http"
2022-12-21 18:36:58 +00:00
"net/http/httputil"
2022-07-25 04:08:42 +01:00
"net/netip"
2022-11-02 03:37:13 +00:00
"net/url"
2020-09-17 15:59:55 +01:00
"os"
2021-04-17 05:01:29 +01:00
"os/user"
2021-03-29 18:42:33 +01:00
"path/filepath"
2020-10-21 20:55:03 +01:00
"runtime"
2021-04-08 22:54:25 +01:00
"sort"
2021-03-26 20:44:55 +00:00
"strconv"
2020-02-05 22:16:58 +00:00
"strings"
"sync"
2021-04-02 16:21:40 +01:00
"sync/atomic"
2020-02-05 22:16:58 +00:00
"time"
2022-11-07 23:32:53 +00:00
"go4.org/mem"
2022-07-25 04:08:42 +01:00
"go4.org/netipx"
2022-10-04 04:39:45 +01:00
"golang.org/x/exp/slices"
2021-04-13 16:13:46 +01:00
"tailscale.com/client/tailscale/apitype"
2020-02-05 22:16:58 +00:00
"tailscale.com/control/controlclient"
2022-09-26 18:07:28 +01:00
"tailscale.com/doctor"
"tailscale.com/doctor/routetable"
2022-01-24 18:52:57 +00:00
"tailscale.com/envknob"
2021-02-25 05:29:51 +00:00
"tailscale.com/health"
2022-11-11 17:43:49 +00:00
"tailscale.com/health/healthmsg"
2021-08-20 18:34:13 +01:00
"tailscale.com/hostinfo"
2021-02-04 21:12:42 +00:00
"tailscale.com/ipn"
2022-11-23 19:18:18 +00:00
"tailscale.com/ipn/ipnauth"
2020-03-26 05:57:46 +00:00
"tailscale.com/ipn/ipnstate"
2020-04-01 05:48:33 +01:00
"tailscale.com/ipn/policy"
2021-03-25 21:50:21 +00:00
"tailscale.com/net/dns"
2022-09-05 19:36:30 +01:00
"tailscale.com/net/dnsfallback"
2020-08-28 05:25:17 +01:00
"tailscale.com/net/interfaces"
2022-03-28 18:24:11 +01:00
"tailscale.com/net/netutil"
2020-07-31 21:27:09 +01:00
"tailscale.com/net/tsaddr"
2021-12-01 04:39:12 +00:00
"tailscale.com/net/tsdial"
2021-03-29 18:42:33 +01:00
"tailscale.com/paths"
2020-02-05 22:16:58 +00:00
"tailscale.com/portlist"
2022-08-04 18:43:49 +01:00
"tailscale.com/syncs"
2020-02-05 22:16:58 +00:00
"tailscale.com/tailcfg"
2022-08-01 23:46:41 +01:00
"tailscale.com/tka"
2021-08-05 22:05:24 +01:00
"tailscale.com/types/dnstype"
2020-02-14 21:09:19 +00:00
"tailscale.com/types/empty"
2020-03-26 05:57:46 +00:00
"tailscale.com/types/key"
2020-02-15 03:23:16 +00:00
"tailscale.com/types/logger"
2021-02-05 23:44:46 +00:00
"tailscale.com/types/netmap"
2021-02-05 23:23:01 +00:00
"tailscale.com/types/persist"
2021-05-27 18:07:17 +01:00
"tailscale.com/types/preftype"
2022-12-01 01:39:43 +00:00
"tailscale.com/types/ptr"
2022-02-22 17:52:49 +00:00
"tailscale.com/types/views"
2021-07-03 05:30:29 +01:00
"tailscale.com/util/deephash"
2021-04-09 23:24:47 +01:00
"tailscale.com/util/dnsname"
2022-10-04 04:39:45 +01:00
"tailscale.com/util/mak"
2021-11-02 21:30:48 +00:00
"tailscale.com/util/multierr"
2021-04-22 08:25:00 +01:00
"tailscale.com/util/osshare"
2022-11-28 18:34:35 +00:00
"tailscale.com/util/set"
2020-11-24 23:35:04 +00:00
"tailscale.com/util/systemd"
2022-11-07 23:32:53 +00:00
"tailscale.com/util/uniq"
2020-02-05 22:16:58 +00:00
"tailscale.com/version"
2021-05-27 18:07:17 +01:00
"tailscale.com/version/distro"
2020-02-05 22:16:58 +00:00
"tailscale.com/wgengine"
"tailscale.com/wgengine/filter"
2021-12-28 21:39:04 +00:00
"tailscale.com/wgengine/magicsock"
2020-05-11 22:02:12 +01:00
"tailscale.com/wgengine/router"
2021-01-29 20:16:36 +00:00
"tailscale.com/wgengine/wgcfg"
2021-02-05 20:44:43 +00:00
"tailscale.com/wgengine/wgcfg/nmcfg"
2020-02-05 22:16:58 +00:00
)
2020-10-20 18:40:12 +01:00
var controlDebugFlags = getControlDebugFlags ( )
func getControlDebugFlags ( ) [ ] string {
2022-01-24 18:52:57 +00:00
if e := envknob . String ( "TS_DEBUG_CONTROL_FLAGS" ) ; e != "" {
2020-10-20 18:40:12 +01:00
return strings . Split ( e , "," )
}
return nil
}
2020-10-19 23:56:59 +01:00
2022-04-15 21:19:13 +01:00
// SSHServer is the interface of the conditionally linked ssh/tailssh.server.
type SSHServer interface {
HandleSSHConn ( net . Conn ) error
2022-04-17 19:49:56 +01:00
// OnPolicyChange is called when the SSH access policy changes,
// so that existing sessions can be re-evaluated for validity
// and closed if they'd no longer be accepted.
OnPolicyChange ( )
2022-05-28 12:33:46 +01:00
// Shutdown is called when tailscaled is shutting down.
Shutdown ( )
2022-04-15 21:19:13 +01:00
}
type newSSHServerFunc func ( logger . Logf , * LocalBackend ) ( SSHServer , error )
var newSSHServer newSSHServerFunc // or nil
// RegisterNewSSHServer lets the conditionally linked ssh/tailssh package register itself.
func RegisterNewSSHServer ( fn newSSHServerFunc ) {
newSSHServer = fn
}
2020-05-19 03:32:20 +01:00
// LocalBackend is the glue between the major pieces of the Tailscale
// network software: the cloud control plane (via controlclient), the
// network data plane (via wgengine), and the user-facing UIs and CLIs
// (collectively called "frontends", via LocalBackend's implementation
// of the Backend interface).
//
// LocalBackend implements the overall state machine for the Tailscale
// application. Frontends, controlclient and wgengine can feed events
// into LocalBackend to advance the state machine, and advancing the
// state machine generates events back out to zero or more components.
2020-02-05 22:16:58 +00:00
type LocalBackend struct {
2020-05-19 03:32:20 +01:00
// Elements that are thread-safe or constant after construction.
2021-03-16 05:20:48 +00:00
ctx context . Context // canceled by Close
ctxCancel context . CancelFunc // cancels ctx
logf logger . Logf // general logging
keyLogf logger . Logf // for printing list of peers on change
statsLogf logger . Logf // for printing peers stats on change
e wgengine . Engine
2022-11-09 05:58:10 +00:00
pm * profileManager
2021-03-16 05:20:48 +00:00
store ipn . StateStore
2021-12-01 04:39:12 +00:00
dialer * tsdial . Dialer // non-nil
2021-03-16 05:20:48 +00:00
backendLogID string
unregisterLinkMon func ( )
unregisterHealthWatch func ( )
portpoll * portlist . Poller // may be nil
portpollOnce sync . Once // guards starting readPoller
gotPortPollRes chan struct { } // closed upon first readPoller result
newDecompressor func ( ) ( controlclient . Decompressor , error )
2021-11-03 16:03:11 +00:00
varRoot string // or empty if SetVarRoot never called
2023-01-05 19:00:42 +00:00
logFlushFunc func ( ) // or nil if SetLogFlusher wasn't called
2022-08-04 05:51:02 +01:00
sshAtomicBool atomic . Bool
2022-05-30 10:06:46 +01:00
shutdownCalled bool // if Shutdown has been called
2020-05-19 03:32:20 +01:00
2022-11-09 05:58:10 +00:00
// lastProfileID tracks the last profile we've seen from the ProfileManager.
// It's used to detect when the user has changed their profile.
lastProfileID ipn . ProfileID
2022-11-07 23:32:53 +00:00
filterAtomic atomic . Pointer [ filter . Filter ]
containsViaIPFuncAtomic syncs . AtomicValue [ func ( netip . Addr ) bool ]
shouldInterceptTCPPortAtomic syncs . AtomicValue [ func ( uint16 ) bool ]
2021-11-25 17:43:39 +00:00
2020-02-05 22:16:58 +00:00
// The mutex protects the following elements.
2020-09-28 23:28:26 +01:00
mu sync . Mutex
2022-04-17 22:49:16 +01:00
filterHash deephash . Sum
2021-04-07 16:39:08 +01:00
httpTestClient * http . Client // for controlclient. nil by default, used by tests.
2021-04-30 15:23:22 +01:00
ccGen clientGen // function for producing controlclient; lazily populated
2022-05-30 10:06:46 +01:00
sshServer SSHServer // or nil, initialized lazily.
2021-02-04 21:12:42 +00:00
notify func ( ipn . Notify )
2021-04-30 04:27:00 +01:00
cc controlclient . Client
2022-06-20 02:00:50 +01:00
ccAuto * controlclient . Auto // if cc is of type *controlclient.Auto
2021-09-03 21:17:46 +01:00
machinePrivKey key . MachinePrivate
2022-08-26 17:45:16 +01:00
tka * tkaState
2021-02-04 21:12:42 +00:00
state ipn . State
2021-04-22 08:25:00 +01:00
capFileSharing bool // whether netMap contains the file sharing capability
2022-11-30 18:34:59 +00:00
capTailnetLock bool // whether netMap contains the tailnet lock capability
2020-06-16 00:04:12 +01:00
// hostinfo is mutated in-place while mu is held.
hostinfo * tailcfg . Hostinfo
// netMap is not mutated in-place once set.
2021-03-25 22:38:40 +00:00
netMap * netmap . NetworkMap
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
nodeByAddr map [ netip . Addr ] * tailcfg . Node
2021-03-25 22:38:40 +00:00
activeLogin string // last logged LoginName from netMap
engineStatus ipn . EngineStatus
tailcfg: add Endpoint, EndpointType, MapRequest.EndpointType
Track endpoints internally with a new tailcfg.Endpoint type that
includes a typed netaddr.IPPort (instead of just a string) and
includes a type for how that endpoint was discovered (STUN, local,
etc).
Use []tailcfg.Endpoint instead of []string internally.
At the last second, send it to the control server as the existing
[]string for endpoints, but also include a new parallel
MapRequest.EndpointType []tailcfg.EndpointType, so the control server
can start filtering out less-important endpoint changes from
new-enough clients. Notably, STUN-discovered endpoints can be filtered
out from 1.6+ clients, as they can discover them amongst each other
via CallMeMaybe disco exchanges started over DERP. And STUN endpoints
change a lot, causing a lot of MapResposne updates. But portmapped
endpoints are worth keeping for now, as they they work right away
without requiring the firewall traversal extra RTT dance.
End result will be less control->client bandwidth. (despite negligible
increase in client->control bandwidth)
Updates tailscale/corp#1543
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-04-12 21:24:29 +01:00
endpoints [ ] tailcfg . Endpoint
2021-03-25 22:38:40 +00:00
blocked bool
2021-11-04 19:19:00 +00:00
keyExpired bool
2021-04-21 21:37:36 +01:00
authURL string // cleared on Notify
authURLSticky string // not cleared on Notify
2021-03-25 22:38:40 +00:00
interact bool
2022-09-01 17:27:06 +01:00
egg bool
2021-03-25 22:38:40 +00:00
prevIfState * interfaces . State
2021-03-30 19:19:42 +01:00
peerAPIServer * peerAPIServer // or nil
2021-03-25 22:38:40 +00:00
peerAPIListeners [ ] * peerAPIListener
2022-02-18 20:55:22 +00:00
loginFlags controlclient . LoginFlags
2021-04-08 22:54:25 +01:00
incomingFiles map [ * incomingFile ] bool
2022-11-28 18:34:35 +00:00
fileWaiters set . HandleSet [ context . CancelFunc ] // of wake-up funcs
notifyWatchers set . HandleSet [ chan * ipn . Notify ]
2022-11-22 19:41:03 +00:00
lastStatusTime time . Time // status.AsOf value of the last processed status update
2021-04-12 22:05:44 +01:00
// directFileRoot, if non-empty, means to write received files
// directly to this directory, without staging them in an
// intermediate buffered directory for "pick-up" later. If
// empty, the files are received in a daemon-owned location
// and the localapi is used to enumerate, download, and delete
// them. This is used on macOS where the GUI lifetime is the
// same as the Network Extension lifetime and we can thus avoid
// double-copying files by writing them to the right location
// immediately.
2022-07-17 18:05:36 +01:00
// It's also used on several NAS platforms (Synology, TrueNAS, etc)
// but in that case DoFinalRename is also set true, which moves the
// *.partial file to its final name on completion.
2021-12-06 20:24:25 +00:00
directFileRoot string
2022-07-17 18:05:36 +01:00
directFileDoFinalRename bool // false on macOS, true on several NAS platforms
2022-10-04 04:39:45 +01:00
componentLogUntil map [ string ] componentLogState
2020-02-05 22:16:58 +00:00
2022-11-07 23:32:53 +00:00
// ServeConfig fields. (also guarded by mu)
2022-11-09 21:15:59 +00:00
lastServeConfJSON mem . RO // last JSON that was parsed into serveConfig
serveConfig ipn . ServeConfigView // or !Valid if none
2022-11-07 23:32:53 +00:00
2022-12-21 18:36:58 +00:00
serveListeners map [ netip . AddrPort ] * serveListener // addrPort => serveListener
serveProxyHandlers sync . Map // string (HTTPHandler.Proxy) => *httputil.ReverseProxy
2022-11-12 02:46:26 +00:00
2020-05-19 03:32:20 +01:00
// statusLock must be held before calling statusChanged.Wait() or
2020-02-05 22:16:58 +00:00
// statusChanged.Broadcast().
statusLock sync . Mutex
statusChanged * sync . Cond
2022-09-23 18:06:55 +01:00
// dialPlan is any dial plan that we've received from the control
// server during a previous connection; it is cleared on logout.
dialPlan atomic . Pointer [ tailcfg . ControlDialPlan ]
2022-10-06 19:09:13 +01:00
// tkaSyncLock is used to make tkaSyncIfNeeded an exclusive
// section. This is needed to stop two map-responses in quick succession
// from racing each other through TKA sync logic / RPCs.
//
// tkaSyncLock MUST be taken before mu (or inversely, mu must not be held
// at the moment that tkaSyncLock is taken).
tkaSyncLock sync . Mutex
2020-02-05 22:16:58 +00:00
}
2021-04-30 15:23:22 +01:00
// clientGen is a func that creates a control plane client.
// It's the type used by LocalBackend.SetControlClientGetterForTesting.
2021-04-30 04:27:00 +01:00
type clientGen func ( controlclient . Options ) ( controlclient . Client , error )
2020-02-03 18:35:52 +00:00
// NewLocalBackend returns a new LocalBackend that is ready to run,
// but is not actually running.
2021-12-01 04:39:12 +00:00
//
// If dialer is nil, a new one is made.
2022-11-09 05:58:10 +00:00
func NewLocalBackend ( logf logger . Logf , logid string , store ipn . StateStore , stateKey ipn . StateKey , dialer * tsdial . Dialer , e wgengine . Engine , loginFlags controlclient . LoginFlags ) ( * LocalBackend , error ) {
2020-02-05 22:16:58 +00:00
if e == nil {
2021-12-01 04:39:12 +00:00
panic ( "ipn.NewLocalBackend: engine must not be nil" )
}
2022-02-14 16:32:23 +00:00
2022-11-09 05:58:10 +00:00
pm , err := newProfileManager ( store , logf , stateKey )
if err != nil {
return nil , err
}
2022-02-14 16:32:23 +00:00
hi := hostinfo . New ( )
2022-02-18 04:41:49 +00:00
logf . JSON ( 1 , "Hostinfo" , hi )
2022-02-13 04:42:38 +00:00
envknob . LogCurrent ( logf )
2021-12-01 04:39:12 +00:00
if dialer == nil {
2022-09-30 22:15:17 +01:00
dialer = & tsdial . Dialer { Logf : logf }
2020-02-05 22:16:58 +00:00
}
2021-04-22 08:25:00 +01:00
osshare . SetFileSharingEnabled ( false , logf )
2020-03-14 03:53:58 +00:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
2020-02-05 22:16:58 +00:00
portpoll , err := portlist . NewPoller ( )
if err != nil {
2020-04-11 16:35:34 +01:00
logf ( "skipping portlist: %s" , err )
2020-02-05 22:16:58 +00:00
}
2020-02-25 15:36:32 +00:00
b := & LocalBackend {
2020-10-14 22:07:40 +01:00
ctx : ctx ,
ctxCancel : cancel ,
logf : logf ,
keyLogf : logger . LogOnChange ( logf , 5 * time . Minute , time . Now ) ,
2020-10-29 22:26:10 +00:00
statsLogf : logger . LogOnChange ( logf , 5 * time . Minute , time . Now ) ,
2020-10-14 22:07:40 +01:00
e : e ,
2022-11-09 05:58:10 +00:00
pm : pm ,
store : pm . Store ( ) ,
2021-12-01 04:39:12 +00:00
dialer : dialer ,
2020-10-14 22:07:40 +01:00
backendLogID : logid ,
2021-02-04 21:12:42 +00:00
state : ipn . NoState ,
2020-10-14 22:07:40 +01:00
portpoll : portpoll ,
gotPortPollRes : make ( chan struct { } ) ,
2022-02-18 20:55:22 +00:00
loginFlags : loginFlags ,
2020-02-05 22:16:58 +00:00
}
2021-12-01 04:39:12 +00:00
2021-11-25 17:43:39 +00:00
// Default filter blocks everything and logs nothing, until Start() is called.
2022-07-25 04:08:42 +01:00
b . setFilter ( filter . NewAllowNone ( logf , & netipx . IPSet { } ) )
2021-11-25 17:43:39 +00:00
2022-11-07 23:32:53 +00:00
b . setTCPPortsIntercepted ( nil )
2020-02-05 22:16:58 +00:00
b . statusChanged = sync . NewCond ( & b . statusLock )
2021-07-19 19:07:42 +01:00
b . e . SetStatusCallback ( b . setWgengineStatus )
2020-02-05 22:16:58 +00:00
2021-03-02 04:45:30 +00:00
linkMon := e . GetLinkMonitor ( )
2021-03-26 04:41:37 +00:00
b . prevIfState = linkMon . InterfaceState ( )
2021-03-02 04:45:30 +00:00
// Call our linkChange code once with the current state, and
// then also whenever it changes:
b . linkChange ( false , linkMon . InterfaceState ( ) )
b . unregisterLinkMon = linkMon . RegisterChangeCallback ( b . linkChange )
2021-03-16 05:20:48 +00:00
b . unregisterHealthWatch = health . RegisterWatcher ( b . onHealthChange )
2021-03-29 23:17:05 +01:00
wiredPeerAPIPort := false
if ig , ok := e . ( wgengine . InternalsGetter ) ; ok {
2022-04-14 22:55:23 +01:00
if tunWrap , _ , _ , ok := ig . GetInternals ( ) ; ok {
2022-01-31 17:20:22 +00:00
tunWrap . PeerAPIPort = b . GetPeerAPIPort
2021-03-29 23:17:05 +01:00
wiredPeerAPIPort = true
}
}
if ! wiredPeerAPIPort {
2022-11-16 17:38:38 +00:00
b . logf ( "[unexpected] failed to wire up PeerAPI port for engine %T" , e )
2021-03-29 23:17:05 +01:00
}
2022-10-04 04:39:45 +01:00
for _ , component := range debuggableComponents {
key := componentStateKey ( component )
2022-11-09 05:58:10 +00:00
if ut , err := ipn . ReadStoreInt ( pm . Store ( ) , key ) ; err == nil {
2022-10-04 04:39:45 +01:00
if until := time . Unix ( ut , 0 ) ; until . After ( time . Now ( ) ) {
// conditional to avoid log spam at start when off
b . SetComponentDebugLogging ( component , until )
}
}
}
2020-02-25 15:36:32 +00:00
return b , nil
2020-02-05 22:16:58 +00:00
}
2022-10-04 04:39:45 +01:00
type componentLogState struct {
until time . Time
timer * time . Timer // if non-nil, the AfterFunc to disable it
}
var debuggableComponents = [ ] string {
"magicsock" ,
}
func componentStateKey ( component string ) ipn . StateKey {
return ipn . StateKey ( "_debug_" + component + "_until" )
}
// SetComponentDebugLogging sets component's debug logging enabled until the until time.
// If until is in the past, the component's debug logging is disabled.
//
// The following components are recognized:
//
// - magicsock
func ( b * LocalBackend ) SetComponentDebugLogging ( component string , until time . Time ) error {
b . mu . Lock ( )
defer b . mu . Unlock ( )
var setEnabled func ( bool )
switch component {
case "magicsock" :
mc , err := b . magicConn ( )
if err != nil {
return err
}
setEnabled = mc . SetDebugLoggingEnabled
}
if setEnabled == nil || ! slices . Contains ( debuggableComponents , component ) {
return fmt . Errorf ( "unknown component %q" , component )
}
timeUnixOrZero := func ( t time . Time ) int64 {
if t . IsZero ( ) {
return 0
}
return t . Unix ( )
}
ipn . PutStoreInt ( b . store , componentStateKey ( component ) , timeUnixOrZero ( until ) )
now := time . Now ( )
on := now . Before ( until )
setEnabled ( on )
var onFor time . Duration
if on {
onFor = until . Sub ( now )
b . logf ( "debugging logging for component %q enabled for %v (until %v)" , component , onFor . Round ( time . Second ) , until . UTC ( ) . Format ( time . RFC3339 ) )
} else {
b . logf ( "debugging logging for component %q disabled" , component )
}
if oldSt , ok := b . componentLogUntil [ component ] ; ok && oldSt . timer != nil {
oldSt . timer . Stop ( )
}
newSt := componentLogState { until : until }
if on {
newSt . timer = time . AfterFunc ( onFor , func ( ) {
// Turn off logging after the timer fires, as long as the state is
// unchanged when the timer actually fires.
b . mu . Lock ( )
defer b . mu . Unlock ( )
if ls := b . componentLogUntil [ component ] ; ls . until == until {
setEnabled ( false )
b . logf ( "debugging logging for component %q disabled (by timer)" , component )
}
} )
}
mak . Set ( & b . componentLogUntil , component , newSt )
return nil
}
2022-10-15 18:31:35 +01:00
// GetComponentDebugLogging gets the time that component's debug logging is
// enabled until, or the zero time if component's time is not currently
// enabled.
func ( b * LocalBackend ) GetComponentDebugLogging ( component string ) time . Time {
b . mu . Lock ( )
defer b . mu . Unlock ( )
now := time . Now ( )
ls := b . componentLogUntil [ component ]
if ls . until . IsZero ( ) || ls . until . Before ( now ) {
return time . Time { }
}
return ls . until
}
2021-12-01 04:39:12 +00:00
// Dialer returns the backend's dialer.
func ( b * LocalBackend ) Dialer ( ) * tsdial . Dialer {
return b . dialer
}
2021-04-12 22:05:44 +01:00
// SetDirectFileRoot sets the directory to download files to directly,
// without buffering them through an intermediate daemon-owned
// tailcfg.UserID-specific directory.
//
// This must be called before the LocalBackend starts being used.
func ( b * LocalBackend ) SetDirectFileRoot ( dir string ) {
b . mu . Lock ( )
defer b . mu . Unlock ( )
b . directFileRoot = dir
}
2021-12-06 20:24:25 +00:00
// SetDirectFileDoFinalRename sets whether the peerapi file server should rename
// a received "name.partial" file to "name" when the download is complete.
//
// This only applies when SetDirectFileRoot is non-empty.
// The default is false.
func ( b * LocalBackend ) SetDirectFileDoFinalRename ( v bool ) {
b . mu . Lock ( )
defer b . mu . Unlock ( )
b . directFileDoFinalRename = v
}
2021-07-16 23:21:00 +01:00
// b.mu must be held.
func ( b * LocalBackend ) maybePauseControlClientLocked ( ) {
if b . cc == nil {
return
}
networkUp := b . prevIfState . AnyInterfaceUp ( )
b . cc . SetPaused ( ( b . state == ipn . Stopped && b . netMap != nil ) || ! networkUp )
}
2021-03-02 04:45:30 +00:00
// linkChange is our link monitor callback, called whenever the network changes.
// major is whether ifst is different than earlier.
2020-08-28 05:25:17 +01:00
func ( b * LocalBackend ) linkChange ( major bool , ifst * interfaces . State ) {
2020-10-02 06:03:25 +01:00
b . mu . Lock ( )
defer b . mu . Unlock ( )
2020-10-05 23:12:35 +01:00
hadPAC := b . prevIfState . HasPAC ( )
b . prevIfState = ifst
2021-07-16 23:21:00 +01:00
b . maybePauseControlClientLocked ( )
2020-10-06 23:22:46 +01:00
2020-10-05 23:12:35 +01:00
// If the PAC-ness of the network changed, reconfig wireguard+route to
// add/remove subnets.
if hadPAC != ifst . HasPAC ( ) {
b . logf ( "linkChange: in state %v; PAC changed from %v->%v" , b . state , hadPAC , ifst . HasPAC ( ) )
switch b . state {
2021-02-04 21:12:42 +00:00
case ipn . NoState , ipn . Stopped :
2020-10-05 23:12:35 +01:00
// Do nothing.
default :
go b . authReconfig ( )
2020-10-02 06:03:25 +01:00
}
}
2021-02-23 04:43:35 +00:00
// If the local network configuration has changed, our filter may
// need updating to tweak default routes.
2022-11-09 05:58:10 +00:00
b . updateFilterLocked ( b . netMap , b . pm . CurrentPrefs ( ) )
2021-03-30 21:49:08 +01:00
2021-06-14 16:59:09 +01:00
if peerAPIListenAsync && b . netMap != nil && b . state == ipn . Running {
2021-03-30 21:49:08 +01:00
want := len ( b . netMap . Addresses )
if len ( b . peerAPIListeners ) < want {
2021-07-06 17:07:18 +01:00
b . logf ( "linkChange: peerAPIListeners too low; trying again" )
2021-03-30 21:49:08 +01:00
go b . initPeerAPIListener ( )
}
}
2020-08-28 05:25:17 +01:00
}
2021-03-16 05:20:48 +00:00
func ( b * LocalBackend ) onHealthChange ( sys health . Subsystem , err error ) {
if err == nil {
b . logf ( "health(%q): ok" , sys )
} else {
b . logf ( "health(%q): error: %v" , sys , err )
}
}
2020-05-19 03:32:20 +01:00
// Shutdown halts the backend and all its sub-components. The backend
// can no longer be used after Shutdown returns.
2020-02-05 22:16:58 +00:00
func ( b * LocalBackend ) Shutdown ( ) {
2020-05-19 03:32:20 +01:00
b . mu . Lock ( )
2022-07-19 04:36:14 +01:00
if b . shutdownCalled {
b . mu . Unlock ( )
return
}
2022-04-25 19:02:59 +01:00
b . shutdownCalled = true
2022-07-19 04:36:14 +01:00
if b . loginFlags & controlclient . LoginEphemeral != 0 {
b . mu . Unlock ( )
ctx , cancel := context . WithTimeout ( b . ctx , 5 * time . Second )
defer cancel ( )
b . LogoutSync ( ctx ) // best effort
b . mu . Lock ( )
}
2021-04-08 05:12:16 +01:00
cc := b . cc
2022-05-28 12:33:46 +01:00
if b . sshServer != nil {
b . sshServer . Shutdown ( )
2022-05-30 10:06:46 +01:00
b . sshServer = nil
2022-05-28 12:33:46 +01:00
}
2022-04-21 03:15:43 +01:00
b . closePeerAPIListenersLocked ( )
2020-05-19 03:32:20 +01:00
b . mu . Unlock ( )
2020-05-21 21:30:20 +01:00
2021-03-01 20:56:03 +00:00
b . unregisterLinkMon ( )
2021-03-16 05:20:48 +00:00
b . unregisterHealthWatch ( )
2021-04-08 05:12:16 +01:00
if cc != nil {
cc . Shutdown ( )
2020-05-19 03:32:20 +01:00
}
2020-05-21 21:30:20 +01:00
b . ctxCancel ( )
2020-02-05 22:16:58 +00:00
b . e . Close ( )
b . e . Wait ( )
}
2022-10-24 01:15:04 +01:00
func stripKeysFromPrefs ( p ipn . PrefsView ) ipn . PrefsView {
2022-11-29 20:00:40 +00:00
if ! p . Valid ( ) || ! p . Persist ( ) . Valid ( ) {
2022-10-24 01:15:04 +01:00
return p
}
p2 := p . AsStruct ( )
p2 . Persist . LegacyFrontendPrivateMachineKey = key . MachinePrivate { }
p2 . Persist . PrivateNodeKey = key . NodePrivate { }
p2 . Persist . OldPrivateNodeKey = key . NodePrivate { }
2022-11-18 19:23:57 +00:00
p2 . Persist . NetworkLockKey = key . NLPrivate { }
2022-10-24 01:15:04 +01:00
return p2 . View ( )
}
2021-04-07 16:27:35 +01:00
// Prefs returns a copy of b's current prefs, with any private keys removed.
2022-10-24 01:15:04 +01:00
func ( b * LocalBackend ) Prefs ( ) ipn . PrefsView {
2021-04-07 16:27:35 +01:00
b . mu . Lock ( )
defer b . mu . Unlock ( )
2022-12-02 08:54:32 +00:00
return b . sanitizedPrefsLocked ( )
2022-12-01 01:39:43 +00:00
}
2022-12-02 08:54:32 +00:00
func ( b * LocalBackend ) sanitizedPrefsLocked ( ) ipn . PrefsView {
2022-11-09 05:58:10 +00:00
return stripKeysFromPrefs ( b . pm . CurrentPrefs ( ) )
2021-04-07 16:27:35 +01:00
}
2020-05-19 03:32:20 +01:00
// Status returns the latest status of the backend and its
// sub-components.
2020-03-26 05:57:46 +00:00
func ( b * LocalBackend ) Status ( ) * ipnstate . Status {
2022-12-19 18:23:47 +00:00
sb := & ipnstate . StatusBuilder { WantPeers : true }
2020-03-26 05:57:46 +00:00
b . UpdateStatus ( sb )
return sb . Status ( )
}
2021-03-19 04:07:58 +00:00
// StatusWithoutPeers is like Status but omits any details
// of peers.
func ( b * LocalBackend ) StatusWithoutPeers ( ) * ipnstate . Status {
2022-12-19 18:23:47 +00:00
sb := & ipnstate . StatusBuilder { WantPeers : false }
b . UpdateStatus ( sb )
2021-03-19 04:07:58 +00:00
return sb . Status ( )
}
2020-05-19 03:32:20 +01:00
// UpdateStatus implements ipnstate.StatusUpdater.
2020-03-26 05:57:46 +00:00
func ( b * LocalBackend ) UpdateStatus ( sb * ipnstate . StatusBuilder ) {
b . e . UpdateStatus ( sb )
2022-12-19 18:23:47 +00:00
var extraLocked func ( * ipnstate . StatusBuilder )
if sb . WantPeers {
extraLocked = b . populatePeerStatusLocked
}
b . updateStatus ( sb , extraLocked )
2021-03-19 04:07:58 +00:00
}
2020-03-26 05:57:46 +00:00
2021-03-19 04:07:58 +00:00
// updateStatus populates sb with status.
//
// extraLocked, if non-nil, is called while b.mu is still held.
func ( b * LocalBackend ) updateStatus ( sb * ipnstate . StatusBuilder , extraLocked func ( * ipnstate . StatusBuilder ) ) {
2020-03-26 05:57:46 +00:00
b . mu . Lock ( )
defer b . mu . Unlock ( )
2021-03-25 22:38:40 +00:00
sb . MutateStatus ( func ( s * ipnstate . Status ) {
s . Version = version . Long
2023-01-13 15:21:08 +00:00
s . TUN = ! wgengine . IsNetstack ( b . e )
2021-03-25 22:38:40 +00:00
s . BackendState = b . state . String ( )
2021-04-21 21:37:36 +01:00
s . AuthURL = b . authURLSticky
2021-09-02 03:27:22 +01:00
if err := health . OverallError ( ) ; err != nil {
switch e := err . ( type ) {
2021-11-02 21:30:48 +00:00
case multierr . Error :
for _ , err := range e . Errors ( ) {
2021-09-02 03:27:22 +01:00
s . Health = append ( s . Health , err . Error ( ) )
}
default :
s . Health = append ( s . Health , err . Error ( ) )
}
}
2022-06-17 20:09:23 +01:00
if m := b . sshOnButUnusableHealthCheckMessageLocked ( ) ; m != "" {
s . Health = append ( s . Health , m )
}
2022-11-19 21:46:08 +00:00
if version . IsUnstableBuild ( ) {
s . Health = append ( s . Health , "This is an unstable (development) version of Tailscale; frequent updates and bugs are likely" )
}
2021-03-25 22:38:40 +00:00
if b . netMap != nil {
2021-06-15 20:12:15 +01:00
s . CertDomains = append ( [ ] string ( nil ) , b . netMap . DNS . CertDomains ... )
2022-02-15 17:36:01 +00:00
s . MagicDNSSuffix = b . netMap . MagicDNSSuffix ( )
if s . CurrentTailnet == nil {
s . CurrentTailnet = & ipnstate . TailnetStatus { }
}
s . CurrentTailnet . MagicDNSSuffix = b . netMap . MagicDNSSuffix ( )
s . CurrentTailnet . MagicDNSEnabled = b . netMap . DNS . Proxied
s . CurrentTailnet . Name = b . netMap . Domain
2022-11-11 17:43:49 +00:00
if prefs := b . pm . CurrentPrefs ( ) ; prefs . Valid ( ) {
if ! prefs . RouteAll ( ) && b . netMap . AnyPeersAdvertiseRoutes ( ) {
s . Health = append ( s . Health , healthmsg . WarnAcceptRoutesOff )
}
if ! prefs . ExitNodeID ( ) . IsZero ( ) {
if exitPeer , ok := b . netMap . PeerWithStableID ( prefs . ExitNodeID ( ) ) ; ok {
var online = false
if exitPeer . Online != nil {
online = * exitPeer . Online
}
s . ExitNodeStatus = & ipnstate . ExitNodeStatus {
ID : prefs . ExitNodeID ( ) ,
Online : online ,
TailscaleIPs : exitPeer . Addresses ,
}
2022-06-07 20:31:10 +01:00
}
}
}
2021-03-25 22:38:40 +00:00
}
} )
sb . MutateSelfStatus ( func ( ss * ipnstate . PeerStatus ) {
2021-12-16 16:06:32 +00:00
ss . Online = health . GetInPollNetMap ( )
2021-11-26 01:11:01 +00:00
if b . netMap != nil {
2022-10-28 17:39:01 +01:00
ss . InNetworkMap = true
2021-11-26 01:11:01 +00:00
ss . HostName = b . netMap . Hostinfo . Hostname
ss . DNSName = b . netMap . Name
ss . UserID = b . netMap . User
if sn := b . netMap . SelfNode ; sn != nil {
2022-10-28 17:39:01 +01:00
peerStatusFromNode ( ss , sn )
2021-11-26 01:11:01 +00:00
if c := sn . Capabilities ; len ( c ) > 0 {
ss . Capabilities = append ( [ ] string ( nil ) , c ... )
}
}
} else {
ss . HostName , _ = os . Hostname ( )
2021-05-07 15:27:14 +01:00
}
2021-03-25 22:38:40 +00:00
for _ , pln := range b . peerAPIListeners {
2021-03-26 20:44:55 +00:00
ss . PeerAPIURL = append ( ss . PeerAPIURL , pln . urlStr )
2021-03-25 22:38:40 +00:00
}
} )
2020-03-26 05:57:46 +00:00
// TODO: hostinfo, and its networkinfo
// TODO: EngineStatus copy (and deprecate it?)
2021-03-19 04:07:58 +00:00
if extraLocked != nil {
extraLocked ( sb )
}
}
func ( b * LocalBackend ) populatePeerStatusLocked ( sb * ipnstate . StatusBuilder ) {
if b . netMap == nil {
return
}
for id , up := range b . netMap . UserProfiles {
sb . AddUser ( id , up )
}
2022-11-09 05:58:10 +00:00
exitNodeID := b . pm . CurrentPrefs ( ) . ExitNodeID ( )
2021-03-19 04:07:58 +00:00
for _ , p := range b . netMap . Peers {
var lastSeen time . Time
if p . LastSeen != nil {
lastSeen = * p . LastSeen
2020-03-26 05:57:46 +00:00
}
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
var tailscaleIPs = make ( [ ] netip . Addr , 0 , len ( p . Addresses ) )
2021-03-19 04:07:58 +00:00
for _ , addr := range p . Addresses {
2022-07-25 04:08:42 +01:00
if addr . IsSingleIP ( ) && tsaddr . IsTailscaleIP ( addr . Addr ( ) ) {
tailscaleIPs = append ( tailscaleIPs , addr . Addr ( ) )
2020-03-26 05:57:46 +00:00
}
}
2022-10-28 17:39:01 +01:00
ps := & ipnstate . PeerStatus {
InNetworkMap : true ,
UserID : p . User ,
TailscaleIPs : tailscaleIPs ,
HostName : p . Hostinfo . Hostname ( ) ,
DNSName : p . Name ,
OS : p . Hostinfo . OS ( ) ,
KeepAlive : p . KeepAlive ,
LastSeen : lastSeen ,
Online : p . Online != nil && * p . Online ,
ShareeNode : p . Hostinfo . ShareeNode ( ) ,
2022-11-09 05:58:10 +00:00
ExitNode : p . StableID != "" && p . StableID == exitNodeID ,
2022-10-28 17:39:01 +01:00
SSH_HostKeys : p . Hostinfo . SSH_HostKeys ( ) . AsSlice ( ) ,
2022-02-22 17:52:49 +00:00
}
2022-10-28 17:39:01 +01:00
peerStatusFromNode ( ps , p )
2022-11-17 20:22:00 +00:00
p4 , p6 := peerAPIPorts ( p )
2022-11-19 21:10:45 +00:00
if u := peerAPIURL ( nodeIP ( p , netip . Addr . Is4 ) , p4 ) ; u != "" {
ps . PeerAPIURL = append ( ps . PeerAPIURL , u )
2022-11-17 20:22:00 +00:00
}
2022-11-19 21:10:45 +00:00
if u := peerAPIURL ( nodeIP ( p , netip . Addr . Is6 ) , p6 ) ; u != "" {
ps . PeerAPIURL = append ( ps . PeerAPIURL , u )
2022-11-17 20:22:00 +00:00
}
2022-10-28 17:39:01 +01:00
sb . AddPeer ( p . Key , ps )
}
}
// peerStatusFromNode copies fields that exist in the Node struct for
// current node and peers into the provided PeerStatus.
func peerStatusFromNode ( ps * ipnstate . PeerStatus , n * tailcfg . Node ) {
ps . ID = n . StableID
ps . Created = n . Created
ps . ExitNodeOption = tsaddr . ContainsExitRoutes ( n . AllowedIPs )
if n . Tags != nil {
v := views . SliceOf ( n . Tags )
ps . Tags = & v
}
if n . PrimaryRoutes != nil {
v := views . IPPrefixSliceOf ( n . PrimaryRoutes )
ps . PrimaryRoutes = & v
2020-03-26 05:57:46 +00:00
}
2023-01-13 23:41:11 +00:00
if n . Expired {
ps . Expired = true
}
2021-01-28 23:29:17 +00:00
}
2020-03-26 05:57:46 +00:00
2021-03-15 21:59:35 +00:00
// WhoIs reports the node and user who owns the node with the given IP:port.
// If the IP address is a Tailscale IP, the provided port may be 0.
2021-01-28 23:29:17 +00:00
// If ok == true, n and u are valid.
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
func ( b * LocalBackend ) WhoIs ( ipp netip . AddrPort ) ( n * tailcfg . Node , u tailcfg . UserProfile , ok bool ) {
2021-01-28 23:29:17 +00:00
b . mu . Lock ( )
defer b . mu . Unlock ( )
2022-07-25 04:08:42 +01:00
n , ok = b . nodeByAddr [ ipp . Addr ( ) ]
2021-01-28 23:29:17 +00:00
if ! ok {
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
var ip netip . Addr
2021-05-15 02:07:28 +01:00
if ipp . Port ( ) != 0 {
2021-03-15 21:59:35 +00:00
ip , ok = b . e . WhoIsIPPort ( ipp )
}
if ! ok {
return nil , u , false
}
n , ok = b . nodeByAddr [ ip ]
if ! ok {
return nil , u , false
}
2021-01-28 23:29:17 +00:00
}
u , ok = b . netMap . UserProfiles [ n . User ]
if ! ok {
return nil , u , false
}
return n , u , true
2020-03-26 05:57:46 +00:00
}
2022-03-18 18:48:40 +00:00
// PeerCaps returns the capabilities that remote src IP has to
// ths current node.
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
func ( b * LocalBackend ) PeerCaps ( src netip . Addr ) [ ] string {
2022-03-18 18:48:40 +00:00
b . mu . Lock ( )
defer b . mu . Unlock ( )
2022-09-19 18:36:07 +01:00
return b . peerCapsLocked ( src )
}
func ( b * LocalBackend ) peerCapsLocked ( src netip . Addr ) [ ] string {
2022-03-18 18:48:40 +00:00
if b . netMap == nil {
return nil
}
2022-08-04 05:31:40 +01:00
filt := b . filterAtomic . Load ( )
if filt == nil {
2022-03-18 18:48:40 +00:00
return nil
}
for _ , a := range b . netMap . Addresses {
if ! a . IsSingleIP ( ) {
continue
}
2022-09-19 18:36:07 +01:00
dst := a . Addr ( )
if dst . BitLen ( ) == src . BitLen ( ) { // match on family
return filt . AppendCaps ( nil , src , dst )
2022-03-18 18:48:40 +00:00
}
}
return nil
}
2020-02-05 22:16:58 +00:00
// SetDecompressor sets a decompression function, which must be a zstd
// reader.
//
// This exists because the iOS/Mac NetworkExtension is very resource
// constrained, and the zstd package is too heavy to fit in the
// constrained RSS limit.
func ( b * LocalBackend ) SetDecompressor ( fn func ( ) ( controlclient . Decompressor , error ) ) {
b . newDecompressor = fn
}
2020-06-16 00:04:12 +01:00
// setClientStatus is the callback invoked by the control client whenever it posts a new status.
// Among other things, this is where we update the netmap, packet filters, DNS and DERP maps.
func ( b * LocalBackend ) setClientStatus ( st controlclient . Status ) {
2020-07-29 02:47:23 +01:00
// The following do not depend on any data for which we need to lock b.
2021-10-26 18:19:35 +01:00
if st . Err != nil {
2020-07-29 02:47:23 +01:00
// TODO(crawshaw): display in the UI.
2021-10-26 18:19:35 +01:00
if errors . Is ( st . Err , io . EOF ) {
2020-12-21 18:58:06 +00:00
b . logf ( "[v1] Received error: EOF" )
2021-11-03 22:42:40 +00:00
return
}
b . logf ( "Received error: %v" , st . Err )
var uerr controlclient . UserVisibleError
if errors . As ( st . Err , & uerr ) {
s := uerr . UserVisibleError ( )
b . send ( ipn . Notify { ErrMessage : & s } )
2020-12-21 18:58:06 +00:00
}
2020-07-29 02:47:23 +01:00
return
}
2021-05-06 04:28:29 +01:00
b . mu . Lock ( )
wasBlocked := b . blocked
2021-11-04 19:19:00 +00:00
keyExpiryExtended := false
if st . NetMap != nil {
wasExpired := b . keyExpired
isExpired := ! st . NetMap . Expiry . IsZero ( ) && st . NetMap . Expiry . Before ( time . Now ( ) )
if wasExpired && ! isExpired {
keyExpiryExtended = true
}
b . keyExpired = isExpired
}
2021-05-06 04:28:29 +01:00
b . mu . Unlock ( )
2021-11-04 19:19:00 +00:00
if keyExpiryExtended && wasBlocked {
// Key extended, unblock the engine
b . blockEngineUpdates ( false )
}
2021-05-06 04:28:29 +01:00
if st . LoginFinished != nil && wasBlocked {
2020-06-16 00:04:12 +01:00
// Auth completed, unblock the engine
b . blockEngineUpdates ( false )
b . authReconfig ( )
2021-02-04 21:12:42 +00:00
b . send ( ipn . Notify { LoginFinished : & empty . Message { } } )
2020-06-16 00:04:12 +01:00
}
2020-07-29 02:47:23 +01:00
// Lock b once and do only the things that require locking.
b . mu . Lock ( )
ipnlocal: don't assume NeedsLogin immediately after StartLogout().
Previously, there was no server round trip required to log out, so when
you asked ipnlocal to Logout(), it could clear the netmap immediately
and switch to NeedsLogin state.
In v1.8, we added a true Logout operation. ipn.Logout() would trigger
an async cc.StartLogout() and *also* immediately switch to NeedsLogin.
Unfortunately, some frontends would see NeedsLogin and immediately
trigger a new StartInteractiveLogin() operation, before the
controlclient auth state machine actually acted on the Logout command,
thus accidentally invalidating the entire logout operation, retaining
the netmap, and violating the user's expectations.
Instead, add a new LogoutFinished signal from controlclient
(paralleling LoginFinished) and, upon starting a logout, don't update
the ipn state machine until it's received.
Updates: #1918 (BUG-2)
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-20 07:46:57 +01:00
if st . LogoutFinished != nil {
2022-11-29 20:00:40 +00:00
if p := b . pm . CurrentPrefs ( ) ; ! p . Persist ( ) . Valid ( ) || p . Persist ( ) . LoginName ( ) == "" {
2022-11-12 12:39:29 +00:00
b . mu . Unlock ( )
return
}
if err := b . pm . DeleteProfile ( b . pm . CurrentProfile ( ) . ID ) ; err != nil {
b . logf ( "error deleting profile: %v" , err )
}
2022-12-05 04:07:03 +00:00
if err := b . resetForProfileChangeLockedOnEntry ( ) ; err != nil {
b . logf ( "resetForProfileChangeLockedOnEntry err: %v" , err )
}
2022-11-12 12:39:29 +00:00
return
ipnlocal: don't assume NeedsLogin immediately after StartLogout().
Previously, there was no server round trip required to log out, so when
you asked ipnlocal to Logout(), it could clear the netmap immediately
and switch to NeedsLogin state.
In v1.8, we added a true Logout operation. ipn.Logout() would trigger
an async cc.StartLogout() and *also* immediately switch to NeedsLogin.
Unfortunately, some frontends would see NeedsLogin and immediately
trigger a new StartInteractiveLogin() operation, before the
controlclient auth state machine actually acted on the Logout command,
thus accidentally invalidating the entire logout operation, retaining
the netmap, and violating the user's expectations.
Instead, add a new LogoutFinished signal from controlclient
(paralleling LoginFinished) and, upon starting a logout, don't update
the ipn state machine until it's received.
Updates: #1918 (BUG-2)
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-20 07:46:57 +01:00
}
2022-11-12 12:39:29 +00:00
prefsChanged := false
2022-11-09 05:58:10 +00:00
prefs := b . pm . CurrentPrefs ( ) . AsStruct ( )
2020-07-29 02:47:23 +01:00
netMap := b . netMap
interact := b . interact
ipn{,/ipnlocal}, cmd/tailscale/cli: don't check pref reverts on initial up
The ipn.NewPrefs func returns a populated ipn.Prefs for historical
reasons. It's not used or as important as it once was, but it hasn't
yet been removed. Meanwhile, it contains some default values that are
used on some platforms. Notably, for this bug (#1725), Windows/Mac use
its Prefs.RouteAll true value (to accept subnets), but Linux users
have always gotten a "false" value for that, because that's what
cmd/tailscale's CLI default flag is _for all operating systems_. That
meant that "tailscale up" was rightfully reporting that the user was
changing an implicit setting: RouteAll was changing from true with
false with the user explicitly saying so.
An obvious fix might be to change ipn.NewPrefs to return
Prefs.RouteAll == false on some platforms, but the logic is
complicated by darwin: we want RouteAll true on windows, android, ios,
and the GUI mac app, but not the CLI tailscaled-on-macOS mode. But
even if we used build tags (e.g. the "redo" build tag) to determine
what the default is, that then means we have duplicated and differing
"defaults" between both the CLI up flags and ipn.NewPrefs. Furthering
that complication didn't seem like a good idea.
So, changing the NewPrefs defaults is too invasive at this stage of
the release, as is removing the NewPrefs func entirely.
Instead, tweak slightly the semantics of the ipn.Prefs.ControlURL
field. This now defines that a ControlURL of the empty string means
both "we're uninitialized" and also "just use the default".
Then, once we have the "empty-string-means-unintialized" semantics,
use that to suppress "tailscale up"'s recent implicit-setting-revert
checking safety net, if we've never initialized Tailscale yet.
And update/add tests.
Fixes #1725
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-04-18 06:50:58 +01:00
if prefs . ControlURL == "" {
// Once we get a message from the control plane, set
// our ControlURL pref explicitly. This causes a
// future "tailscale up" to start checking for
// implicit setting reverts, which it doesn't do when
// ControlURL is blank.
prefs . ControlURL = prefs . ControlURLOrDefault ( )
prefsChanged = true
}
2022-11-10 14:43:59 +00:00
if st . Persist != nil && st . Persist . Valid ( ) {
if ! prefs . Persist . View ( ) . Equals ( * st . Persist ) {
2020-07-29 02:47:23 +01:00
prefsChanged = true
2022-11-10 14:43:59 +00:00
prefs . Persist = st . Persist . AsStruct ( )
2020-07-29 02:47:23 +01:00
}
}
if st . URL != "" {
b . authURL = st . URL
2021-04-21 21:37:36 +01:00
b . authURLSticky = st . URL
2020-07-29 02:47:23 +01:00
}
2021-05-06 04:28:29 +01:00
if wasBlocked && st . LoginFinished != nil {
// Interactive login finished successfully (URL visited).
// After an interactive login, the user always wants
// WantRunning.
2022-10-23 18:07:10 +01:00
if ! prefs . WantRunning || prefs . LoggedOut {
2020-07-29 02:47:23 +01:00
prefsChanged = true
}
2022-10-23 18:07:10 +01:00
prefs . WantRunning = true
prefs . LoggedOut = false
2020-07-29 02:47:23 +01:00
}
2022-10-25 19:01:33 +01:00
if findExitNodeIDLocked ( prefs , st . NetMap ) {
prefsChanged = true
}
2022-12-05 04:07:03 +00:00
// Perform all mutations of prefs based on the netmap here.
if st . NetMap != nil {
if b . updatePersistFromNetMapLocked ( st . NetMap , prefs ) {
prefsChanged = true
}
}
// Prefs will be written out if stale; this is not safe unless locked or cloned.
if prefsChanged {
if err := b . pm . SetPrefs ( prefs . View ( ) ) ; err != nil {
b . logf ( "Failed to save new controlclient state: %v" , err )
}
}
// initTKALocked is dependent on CurrentProfile.ID, which is initialized
// (for new profiles) on the first call to b.pm.SetPrefs.
if err := b . initTKALocked ( ) ; err != nil {
b . logf ( "initTKALocked: %v" , err )
}
// Perform all reconfiguration based on the netmap here.
2022-04-17 22:49:16 +01:00
if st . NetMap != nil {
2022-11-30 18:34:59 +00:00
b . capTailnetLock = hasCapability ( st . NetMap , tailcfg . CapabilityTailnetLockAlpha )
2022-10-25 19:01:33 +01:00
b . mu . Unlock ( ) // respect locking rules for tkaSyncIfNeeded
2022-11-11 06:29:03 +00:00
if err := b . tkaSyncIfNeeded ( st . NetMap , prefs . View ( ) ) ; err != nil {
2022-10-25 19:01:33 +01:00
b . logf ( "[v1] TKA sync error: %v" , err )
}
b . mu . Lock ( )
2022-10-27 21:40:31 +01:00
if b . tka != nil {
head , err := b . tka . authority . Head ( ) . MarshalText ( )
if err != nil {
b . logf ( "[v1] error marshalling tka head: %v" , err )
} else {
b . cc . SetTKAHead ( string ( head ) )
}
} else {
b . cc . SetTKAHead ( "" )
}
2022-10-25 19:01:33 +01:00
if ! envknob . TKASkipSignatureCheck ( ) {
b . tkaFilterNetmapLocked ( st . NetMap )
}
b . setNetMapLocked ( st . NetMap )
2022-11-09 05:58:10 +00:00
b . updateFilterLocked ( st . NetMap , prefs . View ( ) )
2022-04-17 22:49:16 +01:00
}
2022-11-09 05:58:10 +00:00
b . mu . Unlock ( )
2022-11-09 23:28:36 +00:00
2022-11-09 05:58:10 +00:00
// Now complete the lock-free parts of what we started while locked.
if prefsChanged {
2022-12-02 08:54:32 +00:00
b . send ( ipn . Notify { Prefs : ptr . To ( prefs . View ( ) ) } )
2020-06-16 00:04:12 +01:00
}
2022-11-09 05:58:10 +00:00
2020-06-16 00:04:12 +01:00
if st . NetMap != nil {
2022-10-18 21:54:07 +01:00
if envknob . NoLogsNoSupport ( ) && hasCapability ( st . NetMap , tailcfg . CapabilityDataPlaneAuditLogs ) {
2022-11-29 18:51:10 +00:00
msg := "tailnet requires logging to be enabled. Remove --no-logs-no-support from tailscaled command line."
2022-10-18 21:54:07 +01:00
health . SetLocalLogConfigHealth ( errors . New ( msg ) )
// Connecting to this tailnet without logging is forbidden; boot us outta here.
b . mu . Lock ( )
prefs . WantRunning = false
p := prefs . View ( )
if err := b . pm . SetPrefs ( p ) ; err != nil {
b . logf ( "Failed to save new controlclient state: %v" , err )
}
b . mu . Unlock ( )
2022-12-02 08:54:32 +00:00
b . send ( ipn . Notify { ErrMessage : & msg , Prefs : & p } )
2022-10-18 21:54:07 +01:00
return
}
2020-07-29 02:47:23 +01:00
if netMap != nil {
diff := st . NetMap . ConciseDiffFrom ( netMap )
2020-06-16 00:04:12 +01:00
if strings . TrimSpace ( diff ) == "" {
2020-12-21 18:58:06 +00:00
b . logf ( "[v1] netmap diff: (none)" )
2020-06-16 00:04:12 +01:00
} else {
2022-02-13 00:22:33 +00:00
b . logf ( "[v1] netmap diff:\n%v" , diff )
2020-06-16 00:04:12 +01:00
}
}
2020-07-29 02:47:23 +01:00
b . e . SetNetworkMap ( st . NetMap )
2020-09-18 15:44:01 +01:00
b . e . SetDERPMap ( st . NetMap . DERPMap )
2020-07-29 02:47:23 +01:00
2022-09-05 19:36:30 +01:00
// Update our cached DERP map
dnsfallback . UpdateCache ( st . NetMap . DERPMap )
2021-02-04 21:12:42 +00:00
b . send ( ipn . Notify { NetMap : st . NetMap } )
2020-06-16 00:04:12 +01:00
}
if st . URL != "" {
b . logf ( "Received auth URL: %.20v..." , st . URL )
2020-10-27 20:57:10 +00:00
if interact {
2020-06-16 00:04:12 +01:00
b . popBrowserAuthNow ( )
}
}
b . stateMachine ( )
2020-07-29 02:47:23 +01:00
// This is currently (2020-07-28) necessary; conditionally disabling it is fragile!
// This is where netmap information gets propagated to router and magicsock.
b . authReconfig ( )
2020-06-16 00:04:12 +01:00
}
2022-10-23 18:07:10 +01:00
// findExitNodeIDLocked updates prefs to reference an exit node by ID, rather
// than by IP. It returns whether prefs was mutated.
func findExitNodeIDLocked ( prefs * ipn . Prefs , nm * netmap . NetworkMap ) ( prefsChanged bool ) {
2021-12-01 23:33:14 +00:00
if nm == nil {
// No netmap, can't resolve anything.
return false
}
2021-01-21 01:24:16 +00:00
// If we have a desired IP on file, try to find the corresponding
// node.
2022-10-23 18:07:10 +01:00
if ! prefs . ExitNodeIP . IsValid ( ) {
2021-02-25 04:05:23 +00:00
return false
}
2021-01-21 01:24:16 +00:00
2021-02-25 04:05:23 +00:00
// IP takes precedence over ID, so if both are set, clear ID.
2022-10-23 18:07:10 +01:00
if prefs . ExitNodeID != "" {
prefs . ExitNodeID = ""
2021-02-25 04:05:23 +00:00
prefsChanged = true
2021-01-21 01:24:16 +00:00
}
for _ , peer := range nm . Peers {
2021-02-25 04:05:23 +00:00
for _ , addr := range peer . Addresses {
2022-10-23 18:07:10 +01:00
if ! addr . IsSingleIP ( ) || addr . Addr ( ) != prefs . ExitNodeIP {
2021-01-21 01:24:16 +00:00
continue
}
2021-02-25 04:05:23 +00:00
// Found the node being referenced, upgrade prefs to
// reference it directly for next time.
2022-10-23 18:07:10 +01:00
prefs . ExitNodeID = peer . StableID
prefs . ExitNodeIP = netip . Addr { }
2021-02-25 04:05:23 +00:00
return true
2021-01-21 01:24:16 +00:00
}
}
2022-10-23 18:07:10 +01:00
return prefsChanged
2021-01-21 01:24:16 +00:00
}
2020-06-16 00:04:12 +01:00
// 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 ) {
if err != nil {
2021-01-07 04:18:29 +00:00
b . logf ( "wgengine status error: %v" , err )
2021-09-15 20:59:47 +01:00
b . broadcastStatusChanged ( )
2020-06-16 00:04:12 +01:00
return
}
if s == nil {
b . logf ( "[unexpected] non-error wgengine update with status=nil: %v" , s )
2021-09-15 20:59:47 +01:00
b . broadcastStatusChanged ( )
2020-06-16 00:04:12 +01:00
return
}
b . mu . Lock ( )
2022-03-27 04:12:12 +01:00
if s . AsOf . Before ( b . lastStatusTime ) {
// Don't process a status update that is older than the one we have
// already processed. (corp#2579)
b . mu . Unlock ( )
return
}
b . lastStatusTime = s . AsOf
2020-10-29 22:26:10 +00:00
es := b . parseWgStatusLocked ( s )
2021-04-08 05:12:16 +01:00
cc := b . cc
2020-06-16 00:04:12 +01:00
b . engineStatus = es
2021-09-15 20:10:15 +01:00
needUpdateEndpoints := ! endpointsEqual ( s . LocalAddrs , b . endpoints )
if needUpdateEndpoints {
b . endpoints = append ( [ ] tailcfg . Endpoint { } , s . LocalAddrs ... )
}
2020-06-16 00:04:12 +01:00
b . mu . Unlock ( )
2021-04-08 05:12:16 +01:00
if cc != nil {
2021-09-15 20:10:15 +01:00
if needUpdateEndpoints {
2022-06-20 00:31:54 +01:00
cc . UpdateEndpoints ( s . LocalAddrs )
2021-09-15 20:10:15 +01:00
}
2021-07-19 19:07:42 +01:00
b . stateMachine ( )
2020-06-16 00:04:12 +01:00
}
2021-09-15 20:59:47 +01:00
b . broadcastStatusChanged ( )
b . send ( ipn . Notify { Engine : & es } )
}
2021-09-16 20:56:43 +01:00
2021-09-15 20:59:47 +01:00
func ( b * LocalBackend ) broadcastStatusChanged ( ) {
// The sync.Cond docs say: "It is allowed but not required for the caller to hold c.L during the call."
// In this particular case, we must acquire b.statusLock. Otherwise we might broadcast before
// the waiter (in requestEngineStatusAndWait) starts to wait, in which case
// the waiter can get stuck indefinitely. See PR 2865.
2021-09-16 20:56:43 +01:00
b . statusLock . Lock ( )
2020-06-16 00:04:12 +01:00
b . statusChanged . Broadcast ( )
2021-09-16 20:56:43 +01:00
b . statusLock . Unlock ( )
2020-06-16 00:04:12 +01:00
}
2021-09-15 20:10:15 +01:00
func endpointsEqual ( x , y [ ] tailcfg . Endpoint ) bool {
if len ( x ) != len ( y ) {
return false
}
for i := range x {
if x [ i ] != y [ i ] {
return false
}
}
return true
}
2021-04-07 06:11:50 +01:00
func ( b * LocalBackend ) SetNotifyCallback ( notify func ( ipn . Notify ) ) {
b . mu . Lock ( )
defer b . mu . Unlock ( )
b . notify = notify
}
2021-04-07 16:39:08 +01:00
// SetHTTPTestClient sets an alternate HTTP client to use with
// connections to the coordination server. It exists for
// testing. Using nil means to use the default.
func ( b * LocalBackend ) SetHTTPTestClient ( c * http . Client ) {
b . mu . Lock ( )
defer b . mu . Unlock ( )
b . httpTestClient = c
}
2021-04-30 15:23:22 +01:00
// SetControlClientGetterForTesting sets the func that creates a
// control plane client. It can be called at most once, before Start.
func ( b * LocalBackend ) SetControlClientGetterForTesting ( newControlClient func ( controlclient . Options ) ( controlclient . Client , error ) ) {
b . mu . Lock ( )
defer b . mu . Unlock ( )
if b . ccGen != nil {
panic ( "invalid use of SetControlClientGetterForTesting after Start" )
}
b . ccGen = newControlClient
}
func ( b * LocalBackend ) getNewControlClientFunc ( ) clientGen {
b . mu . Lock ( )
defer b . mu . Unlock ( )
if b . ccGen == nil {
// Initialize it rather than just returning the
// default to make any future call to
// SetControlClientGetterForTesting panic.
b . ccGen = func ( opts controlclient . Options ) ( controlclient . Client , error ) {
return controlclient . New ( opts )
}
}
return b . ccGen
}
2021-04-23 18:26:25 +01:00
// startIsNoopLocked reports whether a Start call on this LocalBackend
// with the provided Start Options would be a useless no-op.
//
2022-08-02 17:33:46 +01:00
// TODO(apenwarr): we shouldn't need this. The state machine is now
// nearly clean enough where it can accept a new connection while in
// any state, not just Running, and on any platform. We'd want to add
// a few more tests to state_test.go to ensure this continues to work
// as expected.
ipnlocal: don't assume NeedsLogin immediately after StartLogout().
Previously, there was no server round trip required to log out, so when
you asked ipnlocal to Logout(), it could clear the netmap immediately
and switch to NeedsLogin state.
In v1.8, we added a true Logout operation. ipn.Logout() would trigger
an async cc.StartLogout() and *also* immediately switch to NeedsLogin.
Unfortunately, some frontends would see NeedsLogin and immediately
trigger a new StartInteractiveLogin() operation, before the
controlclient auth state machine actually acted on the Logout command,
thus accidentally invalidating the entire logout operation, retaining
the netmap, and violating the user's expectations.
Instead, add a new LogoutFinished signal from controlclient
(paralleling LoginFinished) and, upon starting a logout, don't update
the ipn state machine until it's received.
Updates: #1918 (BUG-2)
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-20 07:46:57 +01:00
//
2021-04-23 18:26:25 +01:00
// b.mu must be held.
func ( b * LocalBackend ) startIsNoopLocked ( opts ipn . Options ) bool {
ipnlocal: accept a new opts.UpdatePrefs field.
This is needed because the original opts.Prefs field was at some point
subverted for use in frontend->backend state migration for backward
compatibility on some platforms. We still need that feature, but we
also need the feature of providing the full set of prefs from
`tailscale up`, *not* including overwriting the prefs.Persist keys, so
we can't use the original field from `tailscale up`.
`tailscale up` had attempted to compensate for that by doing SetPrefs()
before Start(), but that violates the ipn.Backend contract, which says
you should call Start() before anything else (that's why it's called
Start()). As a result, doing SetPrefs({ControlURL=...,
WantRunning=true}) would cause a connection to the *previous* control
server (because WantRunning=true), and then connect to the *new*
control server only after running Start().
This problem may have been avoided before, but only by pure luck.
It turned out to be relatively harmless since the connection to the old
control server was immediately closed and replaced anyway, but it
created a race condition that could have caused spurious notifications
or rejected keys if the server responded quickly.
As already covered by existing TODOs, a better fix would be to have
Start() get out of the business of state migration altogether. But
we're approaching a release so I want to make the minimum possible fix.
Fixes #1840.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-04 09:26:07 +01:00
// Options has 5 fields; check all of them:
2021-04-23 18:26:25 +01:00
// * FrontendLogID
// * StateKey
// * Prefs
ipnlocal: accept a new opts.UpdatePrefs field.
This is needed because the original opts.Prefs field was at some point
subverted for use in frontend->backend state migration for backward
compatibility on some platforms. We still need that feature, but we
also need the feature of providing the full set of prefs from
`tailscale up`, *not* including overwriting the prefs.Persist keys, so
we can't use the original field from `tailscale up`.
`tailscale up` had attempted to compensate for that by doing SetPrefs()
before Start(), but that violates the ipn.Backend contract, which says
you should call Start() before anything else (that's why it's called
Start()). As a result, doing SetPrefs({ControlURL=...,
WantRunning=true}) would cause a connection to the *previous* control
server (because WantRunning=true), and then connect to the *new*
control server only after running Start().
This problem may have been avoided before, but only by pure luck.
It turned out to be relatively harmless since the connection to the old
control server was immediately closed and replaced anyway, but it
created a race condition that could have caused spurious notifications
or rejected keys if the server responded quickly.
As already covered by existing TODOs, a better fix would be to have
Start() get out of the business of state migration altogether. But
we're approaching a release so I want to make the minimum possible fix.
Fixes #1840.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-04 09:26:07 +01:00
// * UpdatePrefs
2021-04-23 18:26:25 +01:00
// * AuthKey
return b . state == ipn . Running &&
b . hostinfo != nil &&
b . hostinfo . FrontendLogID == opts . FrontendLogID &&
2022-11-09 05:58:10 +00:00
opts . LegacyMigrationPrefs == nil &&
ipnlocal: accept a new opts.UpdatePrefs field.
This is needed because the original opts.Prefs field was at some point
subverted for use in frontend->backend state migration for backward
compatibility on some platforms. We still need that feature, but we
also need the feature of providing the full set of prefs from
`tailscale up`, *not* including overwriting the prefs.Persist keys, so
we can't use the original field from `tailscale up`.
`tailscale up` had attempted to compensate for that by doing SetPrefs()
before Start(), but that violates the ipn.Backend contract, which says
you should call Start() before anything else (that's why it's called
Start()). As a result, doing SetPrefs({ControlURL=...,
WantRunning=true}) would cause a connection to the *previous* control
server (because WantRunning=true), and then connect to the *new*
control server only after running Start().
This problem may have been avoided before, but only by pure luck.
It turned out to be relatively harmless since the connection to the old
control server was immediately closed and replaced anyway, but it
created a race condition that could have caused spurious notifications
or rejected keys if the server responded quickly.
As already covered by existing TODOs, a better fix would be to have
Start() get out of the business of state migration altogether. But
we're approaching a release so I want to make the minimum possible fix.
Fixes #1840.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-04 09:26:07 +01:00
opts . UpdatePrefs == nil &&
2021-04-23 18:26:25 +01:00
opts . AuthKey == ""
}
2020-05-19 03:32:20 +01:00
// Start applies the configuration specified in opts, and starts the
// state machine.
//
// TODO(danderson): this function is trying to do too many things at
// once: it loads state, or imports it, or updates prefs sometimes,
// contains some settings that are one-shot things done by `tailscale
// up` because we had nowhere else to put them, and there's no clear
// guarantee that switching from one user's state to another is
// actually a supported operation (it should be, but it's very unclear
// from the following whether or not that is a safe transition).
2021-02-04 21:12:42 +00:00
func ( b * LocalBackend ) Start ( opts ipn . Options ) error {
2022-11-09 05:58:10 +00:00
if opts . LegacyMigrationPrefs == nil && ! b . pm . CurrentPrefs ( ) . Valid ( ) {
return errors . New ( "no prefs provided" )
2020-02-03 23:58:40 +00:00
}
2022-11-09 05:58:10 +00:00
if opts . LegacyMigrationPrefs != nil {
b . logf ( "Start: %v" , opts . LegacyMigrationPrefs . Pretty ( ) )
2020-02-03 18:35:52 +00:00
} else {
2020-04-11 16:35:34 +01:00
b . logf ( "Start" )
2020-02-03 18:35:52 +00:00
}
2020-02-05 22:16:58 +00:00
2021-04-23 18:26:25 +01:00
b . mu . Lock ( )
2022-11-21 23:11:44 +00:00
if opts . UpdatePrefs != nil {
if err := b . checkPrefsLocked ( opts . UpdatePrefs ) ; err != nil {
b . mu . Unlock ( )
return err
}
} else if opts . LegacyMigrationPrefs != nil {
if err := b . checkPrefsLocked ( opts . LegacyMigrationPrefs ) ; err != nil {
b . mu . Unlock ( )
return err
}
}
2022-11-09 05:58:10 +00:00
profileID := b . pm . CurrentProfile ( ) . ID
2021-04-23 18:26:25 +01:00
// The iOS client sends a "Start" whenever its UI screen comes
// up, just because it wants a netmap. That should be fixed,
// but meanwhile we can make Start cheaper here for such a
// case and not restart the world (which takes a few seconds).
// Instead, just send a notify with the state that iOS needs.
2022-11-09 05:58:10 +00:00
if b . startIsNoopLocked ( opts ) && profileID == b . lastProfileID {
2021-04-23 18:26:25 +01:00
b . logf ( "Start: already running; sending notify" )
nm := b . netMap
state := b . state
2022-11-09 05:58:10 +00:00
p := b . pm . CurrentPrefs ( )
2022-12-02 18:04:00 +00:00
b . mu . Unlock ( )
2021-04-23 18:26:25 +01:00
b . send ( ipn . Notify {
State : & state ,
NetMap : nm ,
2022-11-03 22:46:17 +00:00
Prefs : & p ,
2021-04-23 18:26:25 +01:00
LoginFinished : new ( empty . Message ) ,
} )
return nil
}
2021-08-20 18:34:13 +01:00
hostinfo := hostinfo . New ( )
2020-06-16 00:04:12 +01:00
hostinfo . BackendLogID = b . backendLogID
hostinfo . FrontendLogID = opts . FrontendLogID
2022-08-30 20:30:55 +01:00
hostinfo . Userspace . Set ( wgengine . IsNetstack ( b . e ) )
hostinfo . UserspaceRouter . Set ( wgengine . IsNetstackRouter ( b . e ) )
2020-02-05 22:16:58 +00:00
2021-04-08 05:12:16 +01:00
if b . cc != nil {
2020-02-25 20:30:28 +00:00
// TODO(apenwarr): avoid the need to reinit controlclient.
// This will trigger a full relogin/reconfigure cycle every
// time a Handle reconnects to the backend. Ideally, we
// would send the new Prefs and everything would get back
// into sync with the minimal changes. But that's not how it
// is right now, which is a sign that the code is still too
// complicated.
2022-11-15 04:17:28 +00:00
b . resetControlClientLockedAsync ( )
2020-02-25 20:30:28 +00:00
}
2021-04-07 16:39:08 +01:00
httpTestClient := b . httpTestClient
2020-02-25 20:30:28 +00:00
2020-06-16 00:04:12 +01:00
if b . hostinfo != nil {
2022-05-03 23:07:30 +01:00
hostinfo . Services = b . hostinfo . Services // keep any previous services
2020-02-25 18:04:20 +00:00
}
2020-06-16 00:04:12 +01:00
b . hostinfo = hostinfo
2021-02-04 21:12:42 +00:00
b . state = ipn . NoState
2020-02-03 18:35:52 +00:00
2022-11-09 05:58:10 +00:00
if err := b . migrateStateLocked ( opts . LegacyMigrationPrefs ) ; err != nil {
2020-02-03 18:35:52 +00:00
b . mu . Unlock ( )
return fmt . Errorf ( "loading requested state: %v" , err )
2020-02-03 23:58:40 +00:00
}
2020-02-03 18:35:52 +00:00
ipnlocal: accept a new opts.UpdatePrefs field.
This is needed because the original opts.Prefs field was at some point
subverted for use in frontend->backend state migration for backward
compatibility on some platforms. We still need that feature, but we
also need the feature of providing the full set of prefs from
`tailscale up`, *not* including overwriting the prefs.Persist keys, so
we can't use the original field from `tailscale up`.
`tailscale up` had attempted to compensate for that by doing SetPrefs()
before Start(), but that violates the ipn.Backend contract, which says
you should call Start() before anything else (that's why it's called
Start()). As a result, doing SetPrefs({ControlURL=...,
WantRunning=true}) would cause a connection to the *previous* control
server (because WantRunning=true), and then connect to the *new*
control server only after running Start().
This problem may have been avoided before, but only by pure luck.
It turned out to be relatively harmless since the connection to the old
control server was immediately closed and replaced anyway, but it
created a race condition that could have caused spurious notifications
or rejected keys if the server responded quickly.
As already covered by existing TODOs, a better fix would be to have
Start() get out of the business of state migration altogether. But
we're approaching a release so I want to make the minimum possible fix.
Fixes #1840.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-04 09:26:07 +01:00
if opts . UpdatePrefs != nil {
2022-11-09 05:58:10 +00:00
oldPrefs := b . pm . CurrentPrefs ( )
newPrefs := opts . UpdatePrefs . Clone ( )
2022-11-29 20:00:40 +00:00
newPrefs . Persist = oldPrefs . Persist ( ) . AsStruct ( )
2022-11-09 05:58:10 +00:00
pv := newPrefs . View ( )
if err := b . pm . SetPrefs ( pv ) ; err != nil {
b . logf ( "failed to save UpdatePrefs state: %v" , err )
2021-07-13 22:03:05 +01:00
}
2022-11-16 20:51:26 +00:00
b . setAtomicValuesFromPrefsLocked ( pv )
ipnlocal: accept a new opts.UpdatePrefs field.
This is needed because the original opts.Prefs field was at some point
subverted for use in frontend->backend state migration for backward
compatibility on some platforms. We still need that feature, but we
also need the feature of providing the full set of prefs from
`tailscale up`, *not* including overwriting the prefs.Persist keys, so
we can't use the original field from `tailscale up`.
`tailscale up` had attempted to compensate for that by doing SetPrefs()
before Start(), but that violates the ipn.Backend contract, which says
you should call Start() before anything else (that's why it's called
Start()). As a result, doing SetPrefs({ControlURL=...,
WantRunning=true}) would cause a connection to the *previous* control
server (because WantRunning=true), and then connect to the *new*
control server only after running Start().
This problem may have been avoided before, but only by pure luck.
It turned out to be relatively harmless since the connection to the old
control server was immediately closed and replaced anyway, but it
created a race condition that could have caused spurious notifications
or rejected keys if the server responded quickly.
As already covered by existing TODOs, a better fix would be to have
Start() get out of the business of state migration altogether. But
we're approaching a release so I want to make the minimum possible fix.
Fixes #1840.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-04 09:26:07 +01:00
}
2022-11-09 05:58:10 +00:00
prefs := b . pm . CurrentPrefs ( )
wantRunning := prefs . WantRunning ( )
2021-04-02 16:21:40 +01:00
if wantRunning {
if err := b . initMachineKeyLocked ( ) ; err != nil {
return fmt . Errorf ( "initMachineKeyLocked: %w" , err )
}
}
2022-11-09 05:58:10 +00:00
loggedOut := prefs . LoggedOut ( )
2021-04-30 08:56:11 +01:00
2022-11-22 11:27:05 +00:00
serverURL := prefs . ControlURLOrDefault ( )
2022-11-25 14:50:15 +00:00
if inServerMode := prefs . ForceDaemon ( ) ; inServerMode || runtime . GOOS == "windows" {
b . logf ( "Start: serverMode=%v" , inServerMode )
2020-11-04 18:24:33 +00:00
}
2022-11-16 05:22:09 +00:00
b . applyPrefsToHostinfoLocked ( hostinfo , prefs )
2020-02-18 03:33:01 +00:00
2020-10-27 19:51:48 +00:00
b . setNetMapLocked ( nil )
2022-11-29 20:00:40 +00:00
persistv := prefs . Persist ( ) . AsStruct ( )
2022-11-09 05:58:10 +00:00
if persistv == nil {
persistv = new ( persist . Persist )
}
2022-10-23 18:07:10 +01:00
b . updateFilterLocked ( nil , ipn . PrefsView { } )
2020-02-05 22:16:58 +00:00
b . mu . Unlock ( )
2020-10-14 22:07:40 +01:00
if b . portpoll != nil {
b . portpollOnce . Do ( func ( ) {
go b . portpoll . Run ( b . ctx )
go b . readPoller ( )
// Give the poller a second to get results to
// prevent it from restarting our map poll
// HTTP request (via doSetHostinfoFilterServices >
// cli.SetHostinfo). In practice this is very quick.
t0 := time . Now ( )
timer := time . NewTimer ( time . Second )
select {
case <- b . gotPortPollRes :
2022-02-12 16:05:24 +00:00
b . logf ( "[v1] got initial portlist info in %v" , time . Since ( t0 ) . Round ( time . Millisecond ) )
2020-10-14 22:07:40 +01:00
timer . Stop ( )
case <- timer . C :
b . logf ( "timeout waiting for initial portlist" )
}
} )
}
2022-08-09 18:16:34 +01:00
discoPublic := b . e . DiscoPublicKey ( )
2020-06-19 20:06:49 +01:00
2020-02-05 22:16:58 +00:00
var err error
2021-04-14 17:37:54 +01:00
isNetstack := wgengine . IsNetstackRouter ( b . e )
debugFlags := controlDebugFlags
if isNetstack {
debugFlags = append ( [ ] string { "netstack" } , debugFlags ... )
}
ipnlocal: accept a new opts.UpdatePrefs field.
This is needed because the original opts.Prefs field was at some point
subverted for use in frontend->backend state migration for backward
compatibility on some platforms. We still need that feature, but we
also need the feature of providing the full set of prefs from
`tailscale up`, *not* including overwriting the prefs.Persist keys, so
we can't use the original field from `tailscale up`.
`tailscale up` had attempted to compensate for that by doing SetPrefs()
before Start(), but that violates the ipn.Backend contract, which says
you should call Start() before anything else (that's why it's called
Start()). As a result, doing SetPrefs({ControlURL=...,
WantRunning=true}) would cause a connection to the *previous* control
server (because WantRunning=true), and then connect to the *new*
control server only after running Start().
This problem may have been avoided before, but only by pure luck.
It turned out to be relatively harmless since the connection to the old
control server was immediately closed and replaced anyway, but it
created a race condition that could have caused spurious notifications
or rejected keys if the server responded quickly.
As already covered by existing TODOs, a better fix would be to have
Start() get out of the business of state migration altogether. But
we're approaching a release so I want to make the minimum possible fix.
Fixes #1840.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-04 09:26:07 +01:00
// TODO(apenwarr): The only way to change the ServerURL is to
// re-run b.Start(), because this is the only place we create a
// new controlclient. SetPrefs() allows you to overwrite ServerURL,
// but it won't take effect until the next Start().
2021-04-30 15:23:22 +01:00
cc , err := b . getNewControlClientFunc ( ) ( controlclient . Options {
2021-04-02 16:21:40 +01:00
GetMachinePrivateKey : b . createGetMachinePrivateKeyFunc ( ) ,
Logf : logger . WithPrefix ( b . logf , "control: " ) ,
Persist : * persistv ,
2022-11-22 11:27:05 +00:00
ServerURL : serverURL ,
2021-04-02 16:21:40 +01:00
AuthKey : opts . AuthKey ,
Hostinfo : hostinfo ,
KeepAlive : true ,
NewDecompressor : b . newDecompressor ,
2021-04-07 16:39:08 +01:00
HTTPTestClient : httpTestClient ,
2021-04-02 16:21:40 +01:00
DiscoPublicKey : discoPublic ,
2021-04-14 17:37:54 +01:00
DebugFlags : debugFlags ,
2021-04-02 16:21:40 +01:00
LinkMonitor : b . e . GetLinkMonitor ( ) ,
2022-06-06 21:07:14 +01:00
Pinger : b ,
2022-03-21 21:10:25 +00:00
PopBrowserURL : b . tellClientToBrowseToURL ,
2022-11-24 03:13:41 +00:00
OnClientVersion : b . onClientVersion ,
2022-04-27 19:57:59 +01:00
Dialer : b . Dialer ( ) ,
2022-06-20 02:14:45 +01:00
Status : b . setClientStatus ,
2022-08-27 20:55:41 +01:00
C2NHandler : http . HandlerFunc ( b . handleC2N ) ,
2022-09-23 18:06:55 +01:00
DialPlan : & b . dialPlan , // pointer because it can't be copied
2021-03-31 19:55:21 +01:00
2021-09-16 00:26:38 +01:00
// Don't warn about broken Linux IP forwarding when
2021-03-31 19:55:21 +01:00
// netstack is being used.
2021-04-14 17:37:54 +01:00
SkipIPForwardingCheck : isNetstack ,
2020-02-05 22:16:58 +00:00
} )
if err != nil {
return err
}
b . mu . Lock ( )
2021-04-08 05:17:33 +01:00
b . cc = cc
2022-06-20 02:00:50 +01:00
b . ccAuto , _ = cc . ( * controlclient . Auto )
2020-02-28 20:12:49 +00:00
endpoints := b . endpoints
2022-11-22 18:48:07 +00:00
if err := b . initTKALocked ( ) ; err != nil {
b . logf ( "initTKALocked: %v" , err )
}
2022-10-27 21:40:31 +01:00
var tkaHead string
if b . tka != nil {
head , err := b . tka . authority . Head ( ) . MarshalText ( )
if err != nil {
b . mu . Unlock ( )
return fmt . Errorf ( "marshalling tka head: %w" , err )
}
tkaHead = string ( head )
}
2020-02-05 22:16:58 +00:00
b . mu . Unlock ( )
2020-02-28 20:12:49 +00:00
if endpoints != nil {
2022-06-20 00:31:54 +01:00
cc . UpdateEndpoints ( endpoints )
2020-02-05 22:16:58 +00:00
}
2022-10-27 21:40:31 +01:00
cc . SetTKAHead ( tkaHead )
2020-02-05 22:16:58 +00:00
2020-05-19 03:32:20 +01:00
b . e . SetNetInfoCallback ( b . setNetInfo )
2020-03-24 05:16:16 +00:00
2020-02-05 22:16:58 +00:00
blid := b . backendLogID
2020-04-11 16:35:34 +01:00
b . logf ( "Backend: logs: be:%v fe:%v" , blid , opts . FrontendLogID )
2021-02-04 21:12:42 +00:00
b . send ( ipn . Notify { BackendLogID : & blid } )
2022-12-02 08:54:32 +00:00
b . send ( ipn . Notify { Prefs : & prefs } )
2020-02-05 22:16:58 +00:00
2021-04-30 10:27:37 +01:00
if ! loggedOut && b . hasNodeKey ( ) {
// Even if !WantRunning, we should verify our key, if there
// is one. If you want tailscaled to be completely idle,
// use logout instead.
2021-04-08 05:17:33 +01:00
cc . Login ( nil , controlclient . LoginDefault )
2021-04-02 16:21:40 +01:00
}
2021-04-30 10:27:37 +01:00
b . stateMachine ( )
2020-02-05 22:16:58 +00:00
return nil
}
2022-11-13 15:32:37 +00:00
var warnInvalidUnsignedNodes = health . NewWarnable ( )
2022-04-17 22:49:16 +01:00
// updateFilterLocked updates the packet filter in wgengine based on the
2020-05-19 03:32:20 +01:00
// given netMap and user preferences.
2022-04-17 22:49:16 +01:00
//
// b.mu must be held.
2022-10-23 18:07:10 +01:00
func ( b * LocalBackend ) updateFilterLocked ( netMap * netmap . NetworkMap , prefs ipn . PrefsView ) {
2020-07-31 21:03:00 +01:00
// NOTE(danderson): keep change detection as the first thing in
// this function. Don't try to optimize by returning early, more
// likely than not you'll just end up breaking the change
// detection and end up with the wrong filter installed. This is
// quite hard to debug, so save yourself the trouble.
var (
haveNetmap = netMap != nil
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
addrs [ ] netip . Prefix
2020-11-10 05:33:41 +00:00
packetFilter [ ] filter . Match
2022-07-25 04:08:42 +01:00
localNetsB netipx . IPSetBuilder
logNetsB netipx . IPSetBuilder
2022-10-23 18:07:10 +01:00
shieldsUp = ! prefs . Valid ( ) || prefs . ShieldsUp ( ) // Be conservative when not ready
2020-07-31 21:03:00 +01:00
)
2021-03-10 00:10:30 +00:00
// Log traffic for Tailscale IPs.
logNetsB . AddPrefix ( tsaddr . CGNATRange ( ) )
logNetsB . AddPrefix ( tsaddr . TailscaleULARange ( ) )
logNetsB . RemovePrefix ( tsaddr . ChromeOSVMRange ( ) )
2020-07-31 21:03:00 +01:00
if haveNetmap {
addrs = netMap . Addresses
2021-02-22 22:34:15 +00:00
for _ , p := range addrs {
localNetsB . AddPrefix ( p )
}
2020-07-31 21:03:00 +01:00
packetFilter = netMap . PacketFilter
2022-11-02 20:13:26 +00:00
if packetFilterPermitsUnlockedNodes ( netMap . Peers , packetFilter ) {
err := errors . New ( "server sent invalid packet filter permitting traffic to unlocked nodes; rejecting all packets for safety" )
2022-11-13 15:32:37 +00:00
warnInvalidUnsignedNodes . Set ( err )
2022-11-02 20:13:26 +00:00
packetFilter = nil
} else {
2022-11-13 15:32:37 +00:00
warnInvalidUnsignedNodes . Set ( nil )
2022-11-02 20:13:26 +00:00
}
2020-05-22 03:41:18 +01:00
}
2022-10-23 18:07:10 +01:00
if prefs . Valid ( ) {
ar := prefs . AdvertiseRoutes ( )
for i := 0 ; i < ar . Len ( ) ; i ++ {
r := ar . At ( i )
2021-05-15 02:07:28 +01:00
if r . Bits ( ) == 0 {
2021-02-23 04:43:35 +00:00
// When offering a default route to the world, we
// filter out locally reachable LANs, so that the
// default route effectively appears to be a "guest
// wifi": you get internet access, but to additionally
// get LAN access the LAN(s) need to be offered
// explicitly as well.
2021-12-16 19:15:45 +00:00
localInterfaceRoutes , hostIPs , err := interfaceRoutes ( )
if err != nil {
b . logf ( "getting local interface routes: %v" , err )
continue
}
s , err := shrinkDefaultRoute ( r , localInterfaceRoutes , hostIPs )
2021-02-23 04:43:35 +00:00
if err != nil {
b . logf ( "computing default route filter: %v" , err )
continue
}
localNetsB . AddSet ( s )
} else {
localNetsB . AddPrefix ( r )
2021-03-10 00:10:30 +00:00
// When advertising a non-default route, we assume
// this is a corporate subnet that should be present
// in the audit logs.
logNetsB . AddPrefix ( r )
2021-02-23 04:43:35 +00:00
}
2021-02-22 22:34:15 +00:00
}
2020-07-29 02:47:23 +01:00
}
2021-06-02 17:04:37 +01:00
localNets , _ := localNetsB . IPSet ( )
logNets , _ := logNetsB . IPSet ( )
2022-04-19 00:17:10 +01:00
var sshPol tailcfg . SSHPolicy
if haveNetmap && netMap . SSHPolicy != nil {
sshPol = * netMap . SSHPolicy
}
2020-07-29 02:47:23 +01:00
2022-08-15 19:22:28 +01:00
changed := deephash . Update ( & b . filterHash , & struct {
HaveNetmap bool
Addrs [ ] netip . Prefix
FilterMatch [ ] filter . Match
LocalNets [ ] netipx . IPRange
LogNets [ ] netipx . IPRange
ShieldsUp bool
SSHPolicy tailcfg . SSHPolicy
} { haveNetmap , addrs , packetFilter , localNets . Ranges ( ) , logNets . Ranges ( ) , shieldsUp , sshPol } )
2020-07-29 02:47:23 +01:00
if ! changed {
return
}
2020-05-22 03:41:18 +01:00
2020-08-01 03:07:14 +01:00
if ! haveNetmap {
2022-02-12 16:05:24 +00:00
b . logf ( "[v1] netmap packet filter: (not ready yet)" )
2021-11-25 17:43:39 +00:00
b . setFilter ( filter . NewAllowNone ( b . logf , logNets ) )
2020-08-01 03:07:14 +01:00
return
}
2021-01-22 21:39:53 +00:00
oldFilter := b . e . GetFilter ( )
2020-08-01 03:07:14 +01:00
if shieldsUp {
2022-02-12 16:05:24 +00:00
b . logf ( "[v1] netmap packet filter: (shields up)" )
2021-11-25 17:43:39 +00:00
b . setFilter ( filter . NewShieldsUpFilter ( localNets , logNets , oldFilter , b . logf ) )
2020-08-01 03:07:14 +01:00
} else {
2022-02-12 16:05:24 +00:00
b . logf ( "[v1] netmap packet filter: %v filters" , len ( packetFilter ) )
2021-11-25 17:43:39 +00:00
b . setFilter ( filter . New ( packetFilter , localNets , logNets , oldFilter , b . logf ) )
2020-05-22 03:41:18 +01:00
}
2022-04-17 19:49:56 +01:00
if b . sshServer != nil {
go b . sshServer . OnPolicyChange ( )
}
2020-07-29 02:47:23 +01:00
}
2022-11-02 20:13:26 +00:00
// packetFilterPermitsUnlockedNodes reports any peer in peers with the
// UnsignedPeerAPIOnly bool set true has any of its allowed IPs in the packet
// filter.
//
// If this reports true, the packet filter is invalid (the server is either broken
// or malicious) and should be ignored for safety.
func packetFilterPermitsUnlockedNodes ( peers [ ] * tailcfg . Node , packetFilter [ ] filter . Match ) bool {
var b netipx . IPSetBuilder
var numUnlocked int
for _ , p := range peers {
if ! p . UnsignedPeerAPIOnly {
continue
}
numUnlocked ++
for _ , a := range p . AllowedIPs { // not only addresses!
b . AddPrefix ( a )
}
}
if numUnlocked == 0 {
return false
}
s , err := b . IPSet ( )
if err != nil {
// Shouldn't happen, but if it does, fail closed.
return true
}
for _ , m := range packetFilter {
for _ , r := range m . Srcs {
if ! s . OverlapsPrefix ( r ) {
continue
}
if len ( m . Dsts ) != 0 {
return true
}
}
}
return false
}
2021-11-25 17:43:39 +00:00
func ( b * LocalBackend ) setFilter ( f * filter . Filter ) {
b . filterAtomic . Store ( f )
b . e . SetFilter ( f )
}
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
var removeFromDefaultRoute = [ ] netip . Prefix {
2021-02-23 04:43:35 +00:00
// RFC1918 LAN ranges
2022-07-26 04:55:44 +01:00
netip . MustParsePrefix ( "192.168.0.0/16" ) ,
netip . MustParsePrefix ( "172.16.0.0/12" ) ,
netip . MustParsePrefix ( "10.0.0.0/8" ) ,
2021-03-18 00:04:32 +00:00
// IPv4 link-local
2022-07-26 04:55:44 +01:00
netip . MustParsePrefix ( "169.254.0.0/16" ) ,
2021-03-18 00:04:32 +00:00
// IPv4 multicast
2022-07-26 04:55:44 +01:00
netip . MustParsePrefix ( "224.0.0.0/4" ) ,
2021-02-23 04:43:35 +00:00
// Tailscale IPv4 range
tsaddr . CGNATRange ( ) ,
// IPv6 Link-local addresses
2022-07-26 04:55:44 +01:00
netip . MustParsePrefix ( "fe80::/10" ) ,
2021-03-18 00:04:32 +00:00
// IPv6 multicast
2022-07-26 04:55:44 +01:00
netip . MustParsePrefix ( "ff00::/8" ) ,
2021-02-23 04:43:35 +00:00
// Tailscale IPv6 range
tsaddr . TailscaleULARange ( ) ,
}
2021-07-02 15:15:28 +01:00
// internalAndExternalInterfaces splits interface routes into "internal"
// and "external" sets. Internal routes are those of virtual ethernet
// network interfaces used by guest VMs and containers, such as WSL and
// Docker.
//
// Given that "internal" routes don't leave the device, we choose to
// trust them more, allowing access to them when an Exit Node is enabled.
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
func internalAndExternalInterfaces ( ) ( internal , external [ ] netip . Prefix , err error ) {
2021-10-14 21:40:06 +01:00
il , err := interfaces . GetList ( )
if err != nil {
return nil , nil , err
}
return internalAndExternalInterfacesFrom ( il , runtime . GOOS )
}
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
func internalAndExternalInterfacesFrom ( il interfaces . List , goos string ) ( internal , external [ ] netip . Prefix , err error ) {
2021-10-14 21:40:06 +01:00
// We use an IPSetBuilder here to canonicalize the prefixes
// and to remove any duplicate entries.
2022-07-25 04:08:42 +01:00
var internalBuilder , externalBuilder netipx . IPSetBuilder
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
if err := il . ForeachInterfaceAddress ( func ( iface interfaces . Interface , pfx netip . Prefix ) {
2022-07-25 04:08:42 +01:00
if tsaddr . IsTailscaleIP ( pfx . Addr ( ) ) {
2021-07-02 15:15:28 +01:00
return
}
if pfx . IsSingleIP ( ) {
return
}
2021-10-14 21:09:06 +01:00
if iface . IsLoopback ( ) {
2021-10-14 21:40:06 +01:00
internalBuilder . AddPrefix ( pfx )
2021-10-14 21:09:06 +01:00
return
}
2021-10-14 21:40:06 +01:00
if goos == "windows" {
2021-07-02 15:15:28 +01:00
// Windows Hyper-V prefixes all MAC addresses with 00:15:5d.
// https://docs.microsoft.com/en-us/troubleshoot/windows-server/virtualization/default-limit-256-dynamic-mac-addresses
//
// This includes WSL2 vEthernet.
// Importantly: by default WSL2 /etc/resolv.conf points to
// a stub resolver running on the host vEthernet IP.
// So enabling exit nodes with the default tailnet
// configuration breaks WSL2 DNS without this.
mac := iface . Interface . HardwareAddr
if len ( mac ) == 6 && mac [ 0 ] == 0x00 && mac [ 1 ] == 0x15 && mac [ 2 ] == 0x5d {
2021-10-14 21:40:06 +01:00
internalBuilder . AddPrefix ( pfx )
2021-07-02 15:15:28 +01:00
return
}
}
2021-10-14 21:40:06 +01:00
externalBuilder . AddPrefix ( pfx )
2021-07-02 15:15:28 +01:00
} ) ; err != nil {
return nil , nil , err
}
2021-10-14 21:40:06 +01:00
iSet , err := internalBuilder . IPSet ( )
if err != nil {
return nil , nil , err
}
eSet , err := externalBuilder . IPSet ( )
if err != nil {
return nil , nil , err
}
2021-07-02 15:15:28 +01:00
2021-10-14 21:40:06 +01:00
return iSet . Prefixes ( ) , eSet . Prefixes ( ) , nil
2021-07-02 15:15:28 +01:00
}
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
func interfaceRoutes ( ) ( ips * netipx . IPSet , hostIPs [ ] netip . Addr , err error ) {
2022-07-25 04:08:42 +01:00
var b netipx . IPSetBuilder
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
if err := interfaces . ForeachInterfaceAddress ( func ( _ interfaces . Interface , pfx netip . Prefix ) {
2022-07-25 04:08:42 +01:00
if tsaddr . IsTailscaleIP ( pfx . Addr ( ) ) {
2021-02-23 04:43:35 +00:00
return
}
if pfx . IsSingleIP ( ) {
return
}
2022-07-25 04:08:42 +01:00
hostIPs = append ( hostIPs , pfx . Addr ( ) )
2021-04-08 23:56:51 +01:00
b . AddPrefix ( pfx )
} ) ; err != nil {
return nil , nil , err
}
2021-06-02 17:04:37 +01:00
ipSet , _ := b . IPSet ( )
return ipSet , hostIPs , nil
2021-04-08 23:56:51 +01:00
}
// shrinkDefaultRoute returns an IPSet representing the IPs in route,
2021-12-16 19:15:45 +00:00
// minus those in removeFromDefaultRoute and localInterfaceRoutes,
// plus the IPs in hostIPs.
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
func shrinkDefaultRoute ( route netip . Prefix , localInterfaceRoutes * netipx . IPSet , hostIPs [ ] netip . Addr ) ( * netipx . IPSet , error ) {
2022-07-25 04:08:42 +01:00
var b netipx . IPSetBuilder
2021-04-08 23:56:51 +01:00
// Add the default route.
b . AddPrefix ( route )
// Remove the local interface routes.
2021-12-16 19:15:45 +00:00
b . RemoveSet ( localInterfaceRoutes )
2021-03-18 00:04:32 +00:00
// Having removed all the LAN subnets, re-add the hosts's own
// IPs. It's fine for clients to connect to an exit node's public
// IP address, just not the attached subnet.
//
// Truly forbidden subnets (in removeFromDefaultRoute) will still
// be stripped back out by the next step.
for _ , ip := range hostIPs {
if route . Contains ( ip ) {
b . Add ( ip )
}
}
2021-02-23 04:43:35 +00:00
for _ , pfx := range removeFromDefaultRoute {
b . RemovePrefix ( pfx )
}
2021-06-02 17:04:37 +01:00
return b . IPSet ( )
2021-02-23 04:43:35 +00:00
}
2020-07-29 02:47:23 +01:00
// dnsCIDRsEqual determines whether two CIDR lists are equal
// for DNS map construction purposes (that is, only the first entry counts).
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
func dnsCIDRsEqual ( newAddr , oldAddr [ ] netip . Prefix ) bool {
2020-07-29 02:47:23 +01:00
if len ( newAddr ) != len ( oldAddr ) {
return false
2020-02-05 22:16:58 +00:00
}
2020-07-29 02:47:23 +01:00
if len ( newAddr ) == 0 || newAddr [ 0 ] == oldAddr [ 0 ] {
return true
}
return false
}
// dnsMapsEqual determines whether the new and the old network map
// induce the same DNS map. It does so without allocating memory,
// at the expense of giving false negatives if peers are reordered.
2021-02-05 23:44:46 +00:00
func dnsMapsEqual ( new , old * netmap . NetworkMap ) bool {
2020-07-29 02:47:23 +01:00
if ( old == nil ) != ( new == nil ) {
return false
}
if old == nil && new == nil {
return true
}
if len ( new . Peers ) != len ( old . Peers ) {
return false
}
if new . Name != old . Name {
return false
}
if ! dnsCIDRsEqual ( new . Addresses , old . Addresses ) {
return false
}
for i , newPeer := range new . Peers {
oldPeer := old . Peers [ i ]
if newPeer . Name != oldPeer . Name {
return false
}
if ! dnsCIDRsEqual ( newPeer . Addresses , oldPeer . Addresses ) {
return false
}
}
return true
2020-02-05 22:16:58 +00:00
}
2020-05-19 03:32:20 +01:00
// readPoller is a goroutine that receives service lists from
// b.portpoll and propagates them into the controlclient's HostInfo.
2020-04-29 10:23:29 +01:00
func ( b * LocalBackend ) readPoller ( ) {
2020-10-14 22:07:40 +01:00
n := 0
2020-02-05 22:16:58 +00:00
for {
2022-10-22 06:39:59 +01:00
ports , ok := <- b . portpoll . Updates ( )
2020-03-14 03:53:58 +00:00
if ! ok {
return
2020-02-05 22:16:58 +00:00
}
sl := [ ] tailcfg . Service { }
for _ , p := range ports {
s := tailcfg . Service {
2020-04-01 05:48:33 +01:00
Proto : tailcfg . ServiceProto ( p . Proto ) ,
2020-02-05 22:16:58 +00:00
Port : p . Port ,
Description : p . Process ,
}
2020-04-01 05:48:33 +01:00
if policy . IsInterestingService ( s , version . OS ( ) ) {
sl = append ( sl , s )
}
2020-02-05 22:16:58 +00:00
}
b . mu . Lock ( )
2020-06-16 00:04:12 +01:00
if b . hostinfo == nil {
b . hostinfo = new ( tailcfg . Hostinfo )
2020-02-25 19:01:20 +00:00
}
2020-06-16 00:04:12 +01:00
b . hostinfo . Services = sl
hi := b . hostinfo
2020-02-05 22:16:58 +00:00
b . mu . Unlock ( )
2020-04-29 10:23:29 +01:00
b . doSetHostinfoFilterServices ( hi )
2020-10-14 22:07:40 +01:00
n ++
if n == 1 {
close ( b . gotPortPollRes )
}
2020-02-05 22:16:58 +00:00
}
}
2022-11-22 19:41:03 +00:00
// WatchNotifications subscribes to the ipn.Notify message bus notification
// messages.
//
// WatchNotifications blocks until ctx is done.
//
// The provided fn will only be called with non-nil pointers. The caller must
// not modify roNotify. If fn returns false, the watch also stops.
//
// Failure to consume many notifications in a row will result in dropped
// notifications. There is currently (2022-11-22) no mechanism provided to
// detect when a message has been dropped.
2022-11-26 20:19:16 +00:00
func ( b * LocalBackend ) WatchNotifications ( ctx context . Context , mask ipn . NotifyWatchOpt , fn func ( roNotify * ipn . Notify ) ( keepGoing bool ) ) {
2022-11-22 19:41:03 +00:00
ch := make ( chan * ipn . Notify , 128 )
2022-12-21 20:44:51 +00:00
origFn := fn
if mask & ipn . NotifyNoPrivateKeys != 0 {
fn = func ( n * ipn . Notify ) bool {
if n . NetMap == nil || n . NetMap . PrivateKey . IsZero ( ) {
return origFn ( n )
}
// The netmap in n is shared across all watchers, so to mutate it for a
// single watcher we have to clone the notify and the netmap. We can
// make shallow clones, at least.
nm2 := * n . NetMap
n2 := * n
n2 . NetMap = & nm2
n2 . NetMap . PrivateKey = key . NodePrivate { }
return origFn ( & n2 )
}
}
2022-12-01 01:39:43 +00:00
var ini * ipn . Notify
2020-02-25 20:30:28 +00:00
b . mu . Lock ( )
2022-12-01 01:39:43 +00:00
const initialBits = ipn . NotifyInitialState | ipn . NotifyInitialPrefs | ipn . NotifyInitialNetMap
if mask & initialBits != 0 {
2022-12-02 03:06:21 +00:00
ini = & ipn . Notify { Version : version . Long }
2022-12-01 01:39:43 +00:00
if mask & ipn . NotifyInitialState != 0 {
ini . State = ptr . To ( b . state )
if b . state == ipn . NeedsLogin {
ini . BrowseToURL = ptr . To ( b . authURLSticky )
}
}
if mask & ipn . NotifyInitialPrefs != 0 {
2022-12-02 08:54:32 +00:00
ini . Prefs = ptr . To ( b . sanitizedPrefsLocked ( ) )
2022-12-01 01:39:43 +00:00
}
if mask & ipn . NotifyInitialNetMap != 0 {
ini . NetMap = b . netMap
}
}
2022-11-28 18:34:35 +00:00
handle := b . notifyWatchers . Add ( ch )
2020-02-25 20:30:28 +00:00
b . mu . Unlock ( )
2022-12-01 01:39:43 +00:00
2022-11-22 19:41:03 +00:00
defer func ( ) {
b . mu . Lock ( )
delete ( b . notifyWatchers , handle )
b . mu . Unlock ( )
} ( )
2020-02-25 20:30:28 +00:00
2022-12-01 01:39:43 +00:00
if ini != nil {
if ! fn ( ini ) {
return
}
}
2022-11-26 20:19:16 +00:00
// The GUI clients want to know when peers become active or inactive.
// They've historically got this information by polling for it, which is
// wasteful. As a step towards making it efficient, they now set this
// NotifyWatchEngineUpdates bit to ask for us to send it to them only on
// change. That's not yet (as of 2022-11-26) plumbed everywhere in
// tailscaled yet, so just do the polling here. This ends up causing all IPN
// bus watchers to get the notification every 2 seconds instead of just the
// GUI client's bus watcher, but in practice there's only 1 total connection
// anyway. And if we're polling, at least the client isn't making a new HTTP
// request every 2 seconds.
// TODO(bradfitz): plumb this further and only send a Notify on change.
if mask & ipn . NotifyWatchEngineUpdates != 0 {
2022-12-01 01:31:36 +00:00
ctx , cancel := context . WithCancel ( ctx )
defer cancel ( )
2022-11-26 20:19:16 +00:00
go b . pollRequestEngineStatus ( ctx )
}
2022-11-22 19:41:03 +00:00
for {
select {
case <- ctx . Done ( ) :
return
case n := <- ch :
if ! fn ( n ) {
return
}
}
2021-03-30 19:19:42 +01:00
}
2022-11-22 19:41:03 +00:00
}
2021-03-30 19:19:42 +01:00
2022-11-26 20:19:16 +00:00
// pollRequestEngineStatus calls b.RequestEngineStatus every 2 seconds until ctx
// is done.
func ( b * LocalBackend ) pollRequestEngineStatus ( ctx context . Context ) {
ticker := time . NewTicker ( 2 * time . Second )
defer ticker . Stop ( )
for {
select {
case <- ticker . C :
b . RequestEngineStatus ( )
case <- ctx . Done ( ) :
return
}
}
}
2022-12-03 04:13:36 +00:00
// DebugNotify injects a fake notify message to clients.
//
// It should only be used via the LocalAPI's debug handler.
func ( b * LocalBackend ) DebugNotify ( n ipn . Notify ) {
b . send ( n )
}
2022-11-22 19:41:03 +00:00
// send delivers n to the connected frontend and any API watchers from
// LocalBackend.WatchNotifications (via the LocalAPI).
//
// If no frontend is connected or API watchers are backed up, the notification
// is dropped without being delivered.
2022-11-29 05:57:44 +00:00
//
2022-12-02 08:54:32 +00:00
// If n contains Prefs, those will be sanitized before being delivered.
//
2022-11-29 05:57:44 +00:00
// b.mu must not be held.
2022-11-22 19:41:03 +00:00
func ( b * LocalBackend ) send ( n ipn . Notify ) {
2022-12-02 08:54:32 +00:00
if n . Prefs != nil {
n . Prefs = ptr . To ( stripKeysFromPrefs ( * n . Prefs ) )
}
2022-12-03 04:13:36 +00:00
if n . Version == "" {
n . Version = version . Long
}
2022-11-22 19:41:03 +00:00
b . mu . Lock ( )
notifyFunc := b . notify
apiSrv := b . peerAPIServer
2021-12-01 23:00:23 +00:00
if apiSrv . hasFilesWaiting ( ) {
2021-04-09 15:57:32 +01:00
n . FilesWaiting = & empty . Message { }
}
2022-11-22 19:41:03 +00:00
for _ , ch := range b . notifyWatchers {
select {
case ch <- & n :
default :
// Drop the notification if the channel is full.
}
}
b . mu . Unlock ( )
if notifyFunc != nil {
notifyFunc ( n )
}
2021-04-08 22:54:25 +01:00
}
func ( b * LocalBackend ) sendFileNotify ( ) {
var n ipn . Notify
b . mu . Lock ( )
2022-11-20 21:25:54 +00:00
for _ , wakeWaiter := range b . fileWaiters {
wakeWaiter ( )
}
2021-04-08 22:54:25 +01:00
notifyFunc := b . notify
apiSrv := b . peerAPIServer
if notifyFunc == nil || apiSrv == nil {
b . mu . Unlock ( )
return
}
2021-04-09 15:57:32 +01:00
// Make sure we always set n.IncomingFiles non-nil so it gets encoded
// in JSON to clients. They distinguish between empty and non-nil
// to know whether a Notify should be able about files.
n . IncomingFiles = make ( [ ] ipn . PartialFile , 0 )
2021-04-08 22:54:25 +01:00
for f := range b . incomingFiles {
n . IncomingFiles = append ( n . IncomingFiles , f . PartialFile ( ) )
}
b . mu . Unlock ( )
sort . Slice ( n . IncomingFiles , func ( i , j int ) bool {
return n . IncomingFiles [ i ] . Started . Before ( n . IncomingFiles [ j ] . Started )
} )
b . send ( n )
2020-02-05 22:16:58 +00:00
}
2020-05-19 03:32:20 +01:00
// popBrowserAuthNow shuts down the data plane and sends an auth URL
// to the connected frontend, if any.
2020-02-05 22:16:58 +00:00
func ( b * LocalBackend ) popBrowserAuthNow ( ) {
b . mu . Lock ( )
url := b . authURL
2020-10-27 20:57:10 +00:00
b . interact = false
2021-04-21 21:37:36 +01:00
b . authURL = "" // but NOT clearing authURLSticky
2020-02-05 22:16:58 +00:00
b . mu . Unlock ( )
2020-02-25 22:05:17 +00:00
2020-04-11 16:35:34 +01:00
b . logf ( "popBrowserAuthNow: url=%v" , url != "" )
2020-02-05 22:16:58 +00:00
b . blockEngineUpdates ( true )
b . stopEngineAndWait ( )
2022-03-21 21:10:25 +00:00
b . tellClientToBrowseToURL ( url )
2021-02-04 21:12:42 +00:00
if b . State ( ) == ipn . Running {
b . enterState ( ipn . Starting )
2020-02-05 22:16:58 +00:00
}
}
2022-11-16 16:18:28 +00:00
// validPopBrowserURL reports whether urlStr is a valid value for a
// control server to send in a *URL field.
2022-11-16 17:19:59 +00:00
//
2022-11-16 16:18:28 +00:00
// b.mu must *not* be held.
func ( b * LocalBackend ) validPopBrowserURL ( urlStr string ) bool {
if urlStr == "" {
2022-11-16 17:19:59 +00:00
return false
2022-11-16 16:18:28 +00:00
}
u , err := url . Parse ( urlStr )
if err != nil {
return false
}
switch u . Scheme {
case "https" :
return true
case "http" :
2022-11-22 11:27:05 +00:00
serverURL := b . Prefs ( ) . ControlURLOrDefault ( )
2022-11-16 16:18:28 +00:00
// If the control server is using plain HTTP (likely a dev server),
// then permit http://.
return strings . HasPrefix ( serverURL , "http://" )
}
return false
}
2022-03-21 21:10:25 +00:00
func ( b * LocalBackend ) tellClientToBrowseToURL ( url string ) {
2022-11-16 17:19:59 +00:00
if b . validPopBrowserURL ( url ) {
2022-03-21 21:10:25 +00:00
b . send ( ipn . Notify { BrowseToURL : & url } )
}
}
2022-11-24 03:13:41 +00:00
// onClientVersion is called on MapResponse updates when a MapResponse contains
// a non-nil ClientVersion message.
func ( b * LocalBackend ) onClientVersion ( v * tailcfg . ClientVersion ) {
switch runtime . GOOS {
case "darwin" , "ios" :
// These auto-update well enough, and we haven't converted the
// ClientVersion types to Swift yet, so don't send them in ipn.Notify
// messages.
default :
// But everything else is a Go client and can deal with this field, even
// if they ignore it.
b . send ( ipn . Notify { ClientVersion : v } )
}
}
2021-04-02 16:21:40 +01:00
// For testing lazy machine key generation.
2022-09-14 20:49:39 +01:00
var panicOnMachineKeyGeneration = envknob . RegisterBool ( "TS_DEBUG_PANIC_MACHINE_KEY" )
2021-04-02 16:21:40 +01:00
2021-09-03 21:17:46 +01:00
func ( b * LocalBackend ) createGetMachinePrivateKeyFunc ( ) func ( ) ( key . MachinePrivate , error ) {
2022-08-04 18:43:49 +01:00
var cache syncs . AtomicValue [ key . MachinePrivate ]
2021-09-03 21:17:46 +01:00
return func ( ) ( key . MachinePrivate , error ) {
2022-09-14 20:49:39 +01:00
if panicOnMachineKeyGeneration ( ) {
2021-04-02 16:21:40 +01:00
panic ( "machine key generated" )
}
2022-08-04 18:43:49 +01:00
if v , ok := cache . LoadOk ( ) ; ok {
2021-04-02 16:21:40 +01:00
return v , nil
}
b . mu . Lock ( )
defer b . mu . Unlock ( )
2022-08-04 18:43:49 +01:00
if v , ok := cache . LoadOk ( ) ; ok {
2021-04-02 16:21:40 +01:00
return v , nil
}
if err := b . initMachineKeyLocked ( ) ; err != nil {
2021-09-03 21:17:46 +01:00
return key . MachinePrivate { } , err
2021-04-02 16:21:40 +01:00
}
cache . Store ( b . machinePrivKey )
return b . machinePrivKey , nil
}
}
2020-09-28 23:28:26 +01:00
// initMachineKeyLocked is called to initialize b.machinePrivKey.
//
// b.prefs must already be initialized.
2020-11-04 17:35:58 +00:00
// b.stateKey should be set too, but just for nicer log messages.
2020-09-28 23:28:26 +01:00
// b.mu must be held.
2020-10-21 20:55:03 +01:00
func ( b * LocalBackend ) initMachineKeyLocked ( ) ( err error ) {
2020-09-28 23:28:26 +01:00
if ! b . machinePrivKey . IsZero ( ) {
// Already set.
return nil
}
2021-09-03 21:17:46 +01:00
var legacyMachineKey key . MachinePrivate
2022-11-29 20:00:40 +00:00
if p := b . pm . CurrentPrefs ( ) . Persist ( ) ; p . Valid ( ) {
legacyMachineKey = p . LegacyFrontendPrivateMachineKey ( )
2020-09-28 23:28:26 +01:00
}
2021-02-04 21:12:42 +00:00
keyText , err := b . store . ReadState ( ipn . MachineKeyStateKey )
2020-09-28 23:28:26 +01:00
if err == nil {
if err := b . machinePrivKey . UnmarshalText ( keyText ) ; err != nil {
2021-02-04 21:12:42 +00:00
return fmt . Errorf ( "invalid key in %s key of %v: %w" , ipn . MachineKeyStateKey , b . store , err )
2020-09-28 23:28:26 +01:00
}
if b . machinePrivKey . IsZero ( ) {
2021-02-04 21:12:42 +00:00
return fmt . Errorf ( "invalid zero key stored in %v key of %v" , ipn . MachineKeyStateKey , b . store )
2020-09-28 23:28:26 +01:00
}
2021-09-03 21:17:46 +01:00
if ! legacyMachineKey . IsZero ( ) && ! legacyMachineKey . Equal ( b . machinePrivKey ) {
2020-09-28 23:28:26 +01:00
b . logf ( "frontend-provided legacy machine key ignored; used value from server state" )
}
return nil
}
2021-02-04 21:12:42 +00:00
if err != ipn . ErrStateNotExist {
return fmt . Errorf ( "error reading %v key of %v: %w" , ipn . MachineKeyStateKey , b . store , err )
2020-09-28 23:28:26 +01:00
}
// If we didn't find one already on disk and the prefs already
// have a legacy machine key, use that. Otherwise generate a
// new one.
if ! legacyMachineKey . IsZero ( ) {
b . machinePrivKey = legacyMachineKey
} else {
b . logf ( "generating new machine key" )
2021-09-03 21:17:46 +01:00
b . machinePrivKey = key . NewMachine ( )
2020-09-28 23:28:26 +01:00
}
keyText , _ = b . machinePrivKey . MarshalText ( )
2021-02-04 21:12:42 +00:00
if err := b . store . WriteState ( ipn . MachineKeyStateKey , keyText ) ; err != nil {
2020-09-28 23:28:26 +01:00
b . logf ( "error writing machine key to store: %v" , err )
return err
}
b . logf ( "machine key written to store" )
return nil
}
2022-11-09 05:58:10 +00:00
// migrateStateLocked migrates state from the frontend to the backend.
// It is a no-op if prefs is nil
// b.mu must be held.
func ( b * LocalBackend ) migrateStateLocked ( prefs * ipn . Prefs ) ( err error ) {
if prefs == nil && ! b . pm . CurrentPrefs ( ) . Valid ( ) {
return fmt . Errorf ( "no prefs provided and no current profile" )
2020-02-14 00:38:36 +00:00
}
if prefs != nil {
// Backend owns the state, but frontend is trying to migrate
// state into the backend.
2020-11-04 18:24:33 +00:00
b . logf ( "importing frontend prefs into backend store; frontend prefs: %s" , prefs . Pretty ( ) )
2022-11-09 05:58:10 +00:00
if err := b . pm . SetPrefs ( prefs . View ( ) ) ; err != nil {
2020-02-03 18:35:52 +00:00
return fmt . Errorf ( "store.WriteState: %v" , err )
}
}
2022-11-16 20:51:26 +00:00
b . setAtomicValuesFromPrefsLocked ( b . pm . CurrentPrefs ( ) )
2021-08-26 22:50:55 +01:00
2020-02-03 18:35:52 +00:00
return nil
}
2022-11-07 23:32:53 +00:00
// setTCPPortsIntercepted populates b.shouldInterceptTCPPortAtomic with an
// efficient func for ShouldInterceptTCPPort to use, which is called on every
// incoming packet.
func ( b * LocalBackend ) setTCPPortsIntercepted ( ports [ ] uint16 ) {
slices . Sort ( ports )
uniq . ModifySlice ( & ports )
var f func ( uint16 ) bool
switch len ( ports ) {
case 0 :
f = func ( uint16 ) bool { return false }
case 1 :
f = func ( p uint16 ) bool { return ports [ 0 ] == p }
case 2 :
f = func ( p uint16 ) bool { return ports [ 0 ] == p || ports [ 1 ] == p }
case 3 :
f = func ( p uint16 ) bool { return ports [ 0 ] == p || ports [ 1 ] == p || ports [ 2 ] == p }
default :
if len ( ports ) > 16 {
m := map [ uint16 ] bool { }
for _ , p := range ports {
m [ p ] = true
}
f = func ( p uint16 ) bool { return m [ p ] }
} else {
f = func ( p uint16 ) bool {
for _ , x := range ports {
if p == x {
return true
}
}
return false
}
}
}
b . shouldInterceptTCPPortAtomic . Store ( f )
}
2022-11-16 20:51:26 +00:00
// setAtomicValuesFromPrefsLocked populates sshAtomicBool, containsViaIPFuncAtomic
2022-11-09 05:58:10 +00:00
// and shouldInterceptTCPPortAtomic from the prefs p, which may be !Valid().
2022-11-16 20:51:26 +00:00
func ( b * LocalBackend ) setAtomicValuesFromPrefsLocked ( p ipn . PrefsView ) {
2022-10-23 18:07:10 +01:00
b . sshAtomicBool . Store ( p . Valid ( ) && p . RunSSH ( ) && envknob . CanSSHD ( ) )
2022-03-30 16:47:16 +01:00
2022-10-23 18:07:10 +01:00
if ! p . Valid ( ) {
2022-03-30 16:47:16 +01:00
b . containsViaIPFuncAtomic . Store ( tsaddr . NewContainsIPFunc ( nil ) )
2022-11-07 23:32:53 +00:00
b . setTCPPortsIntercepted ( nil )
2022-11-16 20:51:26 +00:00
b . lastServeConfJSON = mem . B ( nil )
b . serveConfig = ipn . ServeConfigView { }
2022-03-30 16:47:16 +01:00
} else {
2022-10-23 18:07:10 +01:00
b . containsViaIPFuncAtomic . Store ( tsaddr . NewContainsIPFunc ( p . AdvertiseRoutes ( ) . Filter ( tsaddr . IsViaPrefix ) ) )
2022-11-09 05:58:10 +00:00
b . setTCPPortsInterceptedFromNetmapAndPrefsLocked ( p )
2022-03-30 16:47:16 +01:00
}
}
2020-05-19 03:32:20 +01:00
// State returns the backend state machine's current state.
2021-02-04 21:12:42 +00:00
func ( b * LocalBackend ) State ( ) ipn . State {
2020-02-05 22:16:58 +00:00
b . mu . Lock ( )
defer b . mu . Unlock ( )
return b . state
}
2022-11-25 14:02:40 +00:00
// InServerMode reports whether the Tailscale backend is explicitly running in
// "server mode" where it continues to run despite whatever the platform's
// default is. In practice, this is only used on Windows, where the default
// tailscaled behavior is to shut down whenever the GUI disconnects.
//
// On non-Windows platforms, this usually returns false (because people don't
// set unattended mode on other platforms) and also isn't checked on other
// platforms.
//
// TODO(bradfitz): rename to InWindowsUnattendedMode or something? Or make this
// return true on Linux etc and always be called? It's kinda messy now.
2020-11-02 17:52:59 +00:00
func ( b * LocalBackend ) InServerMode ( ) bool {
ipn, ipnserver, cmd/tailscale: add "server mode" support on Windows
This partially (but not yet fully) migrates Windows to tailscaled's
StateStore storage system.
This adds a new bool Pref, ForceDaemon, defined as:
// ForceDaemon specifies whether a platform that normally
// operates in "client mode" (that is, requires an active user
// logged in with the GUI app running) should keep running after the
// GUI ends and/or the user logs out.
//
// The only current applicable platform is Windows. This
// forced Windows to go into "server mode" where Tailscale is
// running even with no users logged in. This might also be
// used for macOS in the future. This setting has no effect
// for Linux/etc, which always operate in daemon mode.
Then, when ForceDaemon becomes true, we now write use the StateStore
to track which user started it in server mode, and store their prefs
under that key.
The ipnserver validates the connections/identities and informs that
LocalBackend which userid is currently in charge.
The GUI can then enable/disable server mode at runtime, without using
the CLI.
But the "tailscale up" CLI was also fixed, so Windows users can use
authkeys or ACL tags, etc.
Updates #275
2020-10-12 22:28:21 +01:00
b . mu . Lock ( )
defer b . mu . Unlock ( )
2022-11-25 14:50:15 +00:00
return b . pm . CurrentPrefs ( ) . ForceDaemon ( )
ipn, ipnserver, cmd/tailscale: add "server mode" support on Windows
This partially (but not yet fully) migrates Windows to tailscaled's
StateStore storage system.
This adds a new bool Pref, ForceDaemon, defined as:
// ForceDaemon specifies whether a platform that normally
// operates in "client mode" (that is, requires an active user
// logged in with the GUI app running) should keep running after the
// GUI ends and/or the user logs out.
//
// The only current applicable platform is Windows. This
// forced Windows to go into "server mode" where Tailscale is
// running even with no users logged in. This might also be
// used for macOS in the future. This setting has no effect
// for Linux/etc, which always operate in daemon mode.
Then, when ForceDaemon becomes true, we now write use the StateStore
to track which user started it in server mode, and store their prefs
under that key.
The ipnserver validates the connections/identities and informs that
LocalBackend which userid is currently in charge.
The GUI can then enable/disable server mode at runtime, without using
the CLI.
But the "tailscale up" CLI was also fixed, so Windows users can use
authkeys or ACL tags, etc.
Updates #275
2020-10-12 22:28:21 +01:00
}
2022-11-23 19:18:18 +00:00
// CheckIPNConnectionAllowed returns an error if the identity in ci should not
// be allowed to connect or make requests to the LocalAPI currently.
//
// Currently (as of 2022-11-23), this is only used on Windows to check if
// we started in server mode and ci is from an identity other than the one
// that started the server.
func ( b * LocalBackend ) CheckIPNConnectionAllowed ( ci * ipnauth . ConnIdentity ) error {
b . mu . Lock ( )
defer b . mu . Unlock ( )
2022-11-25 14:11:06 +00:00
serverModeUid := b . pm . CurrentUserID ( )
2022-11-23 19:18:18 +00:00
if serverModeUid == "" {
// Either this platform isn't a "multi-user" platform or we're not yet
// running as one.
return nil
}
2022-11-25 14:50:15 +00:00
if ! b . pm . CurrentPrefs ( ) . ForceDaemon ( ) {
2022-11-23 19:18:18 +00:00
return nil
}
2022-11-27 17:04:00 +00:00
uid := ci . WindowsUserID ( )
2022-11-23 19:18:18 +00:00
if uid == "" {
return errors . New ( "empty user uid in connection identity" )
}
if uid != serverModeUid {
2022-11-27 17:04:00 +00:00
return fmt . Errorf ( "Tailscale running in server mode (%q); connection from %q not allowed" , b . tryLookupUserName ( string ( serverModeUid ) ) , b . tryLookupUserName ( string ( uid ) ) )
2022-11-23 19:18:18 +00:00
}
return nil
}
2022-11-25 14:11:06 +00:00
// tryLookupUserName tries to look up the username for the uid.
// It returns the username on success, or the UID on failure.
func ( b * LocalBackend ) tryLookupUserName ( uid string ) string {
u , err := ipnauth . LookupUserFromID ( b . logf , uid )
if err != nil {
return uid
}
return u . Username
}
2020-07-13 21:13:11 +01:00
// Login implements Backend.
2022-11-15 04:17:28 +00:00
// As of 2022-11-15, this is only exists for Android.
2021-03-19 17:21:33 +00:00
func ( b * LocalBackend ) Login ( token * tailcfg . Oauth2Token ) {
2020-07-13 21:13:11 +01:00
b . mu . Lock ( )
b . assertClientLocked ( )
2021-04-08 05:12:16 +01:00
cc := b . cc
2020-07-13 21:13:11 +01:00
b . mu . Unlock ( )
2022-02-18 20:55:22 +00:00
cc . Login ( token , b . loginFlags | controlclient . LoginInteractive )
2020-07-13 21:13:11 +01:00
}
2020-05-19 03:32:20 +01:00
// StartLoginInteractive implements Backend. It requests a new
// interactive login from controlclient, unless such a flow is already
// in progress, in which case StartLoginInteractive attempts to pick
// up the in-progress flow where it left off.
2020-02-05 22:16:58 +00:00
func ( b * LocalBackend ) StartLoginInteractive ( ) {
b . mu . Lock ( )
2020-02-25 20:30:28 +00:00
b . assertClientLocked ( )
2020-10-27 20:57:10 +00:00
b . interact = true
2020-02-05 22:16:58 +00:00
url := b . authURL
2021-04-08 05:12:16 +01:00
cc := b . cc
2020-02-05 22:16:58 +00:00
b . mu . Unlock ( )
2020-04-11 16:35:34 +01:00
b . logf ( "StartLoginInteractive: url=%v" , url != "" )
2020-02-05 22:16:58 +00:00
if url != "" {
b . popBrowserAuthNow ( )
} else {
2022-02-18 20:55:22 +00:00
cc . Login ( nil , b . loginFlags | controlclient . LoginInteractive )
2020-02-05 22:16:58 +00:00
}
}
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
func ( b * LocalBackend ) Ping ( ctx context . Context , ip netip . Addr , pingType tailcfg . PingType ) ( * ipnstate . PingResult , error ) {
2022-05-28 05:34:36 +01:00
if pingType == tailcfg . PingPeerAPI {
t0 := time . Now ( )
node , base , err := b . pingPeerAPI ( ctx , ip )
if err != nil && ctx . Err ( ) != nil {
return nil , ctx . Err ( )
}
d := time . Since ( t0 )
pr := & ipnstate . PingResult {
IP : ip . String ( ) ,
NodeIP : ip . String ( ) ,
LatencySeconds : d . Seconds ( ) ,
PeerAPIURL : base ,
}
if err != nil {
pr . Err = err . Error ( )
}
if node != nil {
pr . NodeName = node . Name
}
return pr , nil
}
2022-05-03 22:16:34 +01:00
ch := make ( chan * ipnstate . PingResult , 1 )
2022-04-22 02:49:01 +01:00
b . e . Ping ( ip , pingType , func ( pr * ipnstate . PingResult ) {
2022-05-03 22:16:34 +01:00
select {
case ch <- pr :
default :
}
2020-08-09 22:49:42 +01:00
} )
2022-05-03 22:16:34 +01:00
select {
case pr := <- ch :
return pr , nil
case <- ctx . Done ( ) :
return nil , ctx . Err ( )
}
2020-08-09 22:49:42 +01:00
}
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 05:14:09 +01:00
func ( b * LocalBackend ) pingPeerAPI ( ctx context . Context , ip netip . Addr ) ( peer * tailcfg . Node , peerBase string , err error ) {
2022-05-28 05:34:36 +01:00
ctx , cancel := context . WithTimeout ( ctx , 10 * time . Second )
defer cancel ( )
nm := b . NetMap ( )
if nm == nil {
return nil , "" , errors . New ( "no netmap" )
}
peer , ok := nm . PeerByTailscaleIP ( ip )
if ! ok {
return nil , "" , fmt . Errorf ( "no peer found with Tailscale IP %v" , ip )
}
2023-01-13 19:29:41 +00:00
if peer . Expired {
return nil , "" , errors . New ( "peer's node key has expired" )
}
2022-05-28 05:34:36 +01:00
base := peerAPIBase ( nm , peer )
if base == "" {
2022-11-16 17:38:38 +00:00
return nil , "" , fmt . Errorf ( "no PeerAPI base found for peer %v (%v)" , peer . ID , ip )
2022-05-28 05:34:36 +01:00
}
outReq , err := http . NewRequestWithContext ( ctx , "HEAD" , base , nil )
if err != nil {
return nil , "" , err
}
tr := b . Dialer ( ) . PeerAPITransport ( )
res , err := tr . RoundTrip ( outReq )
if err != nil {
return nil , "" , err
}
defer res . Body . Close ( ) // but unnecessary on HEAD responses
if res . StatusCode != http . StatusOK {
return nil , "" , fmt . Errorf ( "HTTP status %v" , res . Status )
}
return peer , base , nil
}
2020-10-29 22:26:10 +00:00
// parseWgStatusLocked returns an EngineStatus based on s.
//
// b.mu must be held; mostly because the caller is about to anyway, and doing so
// gives us slightly better guarantees about the two peers stats lines not
// being intermixed if there are concurrent calls to our caller.
2021-02-04 21:12:42 +00:00
func ( b * LocalBackend ) parseWgStatusLocked ( s * wgengine . Status ) ( ret ipn . EngineStatus ) {
2020-10-29 22:26:10 +00:00
var peerStats , peerKeys strings . Builder
2020-02-05 22:16:58 +00:00
2020-05-29 17:53:04 +01:00
ret . LiveDERPs = s . DERPs
2021-11-02 01:40:39 +00:00
ret . LivePeers = map [ key . NodePublic ] ipnstate . PeerStatusLite { }
2020-02-05 22:16:58 +00:00
for _ , p := range s . Peers {
2020-05-15 21:13:44 +01:00
if ! p . LastHandshake . IsZero ( ) {
2020-10-29 22:26:10 +00:00
fmt . Fprintf ( & peerStats , "%d/%d " , p . RxBytes , p . TxBytes )
fmt . Fprintf ( & peerKeys , "%s " , p . NodeKey . ShortString ( ) )
2020-05-19 03:32:20 +01:00
ret . NumLive ++
2021-11-02 01:40:39 +00:00
ret . LivePeers [ p . NodeKey ] = p
2020-05-15 21:13:44 +01:00
2020-02-05 22:16:58 +00:00
}
2020-05-19 03:32:20 +01:00
ret . RBytes += p . RxBytes
ret . WBytes += p . TxBytes
2020-05-12 17:14:37 +01:00
}
2020-10-29 22:26:10 +00:00
// [GRINDER STATS LINES] - please don't remove (used for log parsing)
if peerStats . Len ( ) > 0 {
2020-12-21 18:58:06 +00:00
b . keyLogf ( "[v1] peer keys: %s" , strings . TrimSpace ( peerKeys . String ( ) ) )
b . statsLogf ( "[v1] v%v peers: %v" , version . Long , strings . TrimSpace ( peerStats . String ( ) ) )
2020-02-05 22:16:58 +00:00
}
2020-05-19 03:32:20 +01:00
return ret
2020-02-05 22:16:58 +00:00
}
2021-01-11 22:24:32 +00:00
// shouldUploadServices reports whether this node should include services
// in Hostinfo. When the user preferences currently request "shields up"
// mode, all inbound connections are refused, so services are not reported.
// Otherwise, shouldUploadServices respects NetMap.CollectServices.
func ( b * LocalBackend ) shouldUploadServices ( ) bool {
2020-02-05 22:16:58 +00:00
b . mu . Lock ( )
defer b . mu . Unlock ( )
2022-11-09 05:58:10 +00:00
p := b . pm . CurrentPrefs ( )
if ! p . Valid ( ) || b . netMap == nil {
2021-01-11 22:24:32 +00:00
return false // default to safest setting
2020-05-19 03:32:20 +01:00
}
2022-11-09 05:58:10 +00:00
return ! p . ShieldsUp ( ) && b . netMap . CollectServices
2020-02-05 22:16:58 +00:00
}
2022-11-25 11:52:47 +00:00
// SetCurrentUserID is used to implement support for multi-user systems (only
// Windows 2022-11-25). On such systems, the uid is used to determine which
// user's state should be used. The current user is maintained by active
// connections open to the backend.
//
// When the backend initially starts it will typically start with no user. Then,
// the first connection to the backend from the GUI frontend will set the
// current user. Once set, the current user cannot be changed until all previous
// connections are closed. The user is also used to determine which
// LoginProfiles are accessible.
//
// In unattended mode, the backend will start with the user which enabled
// unattended mode. The user must disable unattended mode before the user can be
// changed.
//
// On non-multi-user systems, the uid should be set to empty string.
2022-11-27 17:04:00 +00:00
func ( b * LocalBackend ) SetCurrentUserID ( uid ipn . WindowsUserID ) {
ipn, ipnserver, cmd/tailscale: add "server mode" support on Windows
This partially (but not yet fully) migrates Windows to tailscaled's
StateStore storage system.
This adds a new bool Pref, ForceDaemon, defined as:
// ForceDaemon specifies whether a platform that normally
// operates in "client mode" (that is, requires an active user
// logged in with the GUI app running) should keep running after the
// GUI ends and/or the user logs out.
//
// The only current applicable platform is Windows. This
// forced Windows to go into "server mode" where Tailscale is
// running even with no users logged in. This might also be
// used for macOS in the future. This setting has no effect
// for Linux/etc, which always operate in daemon mode.
Then, when ForceDaemon becomes true, we now write use the StateStore
to track which user started it in server mode, and store their prefs
under that key.
The ipnserver validates the connections/identities and informs that
LocalBackend which userid is currently in charge.
The GUI can then enable/disable server mode at runtime, without using
the CLI.
But the "tailscale up" CLI was also fixed, so Windows users can use
authkeys or ACL tags, etc.
Updates #275
2020-10-12 22:28:21 +01:00
b . mu . Lock ( )
2022-11-25 14:11:06 +00:00
if b . pm . CurrentUserID ( ) == uid {
2022-11-12 07:38:17 +00:00
b . mu . Unlock ( )
return
}
2022-11-25 14:11:06 +00:00
if err := b . pm . SetCurrentUserID ( uid ) ; err != nil {
2022-11-12 07:38:17 +00:00
b . mu . Unlock ( )
return
}
b . resetForProfileChangeLockedOnEntry ( )
ipn, ipnserver, cmd/tailscale: add "server mode" support on Windows
This partially (but not yet fully) migrates Windows to tailscaled's
StateStore storage system.
This adds a new bool Pref, ForceDaemon, defined as:
// ForceDaemon specifies whether a platform that normally
// operates in "client mode" (that is, requires an active user
// logged in with the GUI app running) should keep running after the
// GUI ends and/or the user logs out.
//
// The only current applicable platform is Windows. This
// forced Windows to go into "server mode" where Tailscale is
// running even with no users logged in. This might also be
// used for macOS in the future. This setting has no effect
// for Linux/etc, which always operate in daemon mode.
Then, when ForceDaemon becomes true, we now write use the StateStore
to track which user started it in server mode, and store their prefs
under that key.
The ipnserver validates the connections/identities and informs that
LocalBackend which userid is currently in charge.
The GUI can then enable/disable server mode at runtime, without using
the CLI.
But the "tailscale up" CLI was also fixed, so Windows users can use
authkeys or ACL tags, etc.
Updates #275
2020-10-12 22:28:21 +01:00
}
2022-04-18 17:37:23 +01:00
func ( b * LocalBackend ) CheckPrefs ( p * ipn . Prefs ) error {
b . mu . Lock ( )
defer b . mu . Unlock ( )
return b . checkPrefsLocked ( p )
}
func ( b * LocalBackend ) checkPrefsLocked ( p * ipn . Prefs ) error {
2022-06-17 20:09:23 +01:00
var errs [ ] error
2022-04-18 17:37:23 +01:00
if p . Hostname == "badhostname.tailscale." {
// Keep this one just for testing.
2022-06-17 20:09:23 +01:00
errs = append ( errs , errors . New ( "bad hostname [test]" ) )
2022-04-18 17:37:23 +01:00
}
2022-11-18 09:36:45 +00:00
if err := b . checkProfileNameLocked ( p ) ; err != nil {
errs = append ( errs , err )
}
2022-06-17 20:09:23 +01:00
if err := b . checkSSHPrefsLocked ( p ) ; err != nil {
errs = append ( errs , err )
}
2022-11-18 13:07:55 +00:00
if err := b . checkExitNodePrefsLocked ( p ) ; err != nil {
errs = append ( errs , err )
}
2022-06-17 20:09:23 +01:00
return multierr . New ( errs ... )
}
func ( b * LocalBackend ) checkSSHPrefsLocked ( p * ipn . Prefs ) error {
if ! p . RunSSH {
return nil
}
switch runtime . GOOS {
case "linux" :
if distro . Get ( ) == distro . Synology && ! envknob . UseWIPCode ( ) {
return errors . New ( "The Tailscale SSH server does not run on Synology." )
2022-04-18 17:37:23 +01:00