2020-07-15 07:56:48 -07: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.
package cli
import (
"context"
2020-09-10 19:55:09 -07:00
"errors"
2020-07-15 07:56:48 -07:00
"flag"
"fmt"
"os"
2021-04-10 21:56:18 -07:00
"reflect"
2020-07-15 07:56:48 -07:00
"runtime"
2021-02-24 11:33:03 -08:00
"sort"
2020-07-15 07:56:48 -07:00
"strings"
2020-08-28 20:49:14 +08:00
"sync"
2020-07-15 07:56:48 -07:00
2021-04-26 14:39:49 -07:00
shellquote "github.com/kballard/go-shellquote"
2020-07-15 07:56:48 -07:00
"github.com/peterbourgon/ff/v2/ffcli"
2020-08-10 08:10:15 -07:00
"inet.af/netaddr"
2021-03-18 19:34:59 -07:00
"tailscale.com/client/tailscale"
2020-07-15 07:56:48 -07:00
"tailscale.com/ipn"
2021-04-17 20:35:20 -07:00
"tailscale.com/ipn/ipnstate"
2021-04-16 21:01:29 -07:00
"tailscale.com/safesocket"
2020-07-15 07:56:48 -07:00
"tailscale.com/tailcfg"
2021-04-17 20:35:20 -07:00
"tailscale.com/types/logger"
2021-02-04 13:12:42 -08:00
"tailscale.com/types/preftype"
2020-09-10 19:55:09 -07:00
"tailscale.com/version/distro"
2020-07-15 07:56:48 -07:00
)
var upCmd = & ffcli . Command {
Name : "up" ,
ShortUsage : "up [flags]" ,
2021-04-15 18:33:23 -04:00
ShortHelp : "Connect to Tailscale, logging in if needed" ,
2020-07-15 07:56:48 -07:00
LongHelp : strings . TrimSpace ( `
"tailscale up" connects this machine to your Tailscale network ,
triggering authentication if necessary .
2021-04-10 21:56:18 -07:00
With no flags , "tailscale up" brings the network online without
changing any settings . ( That is , it ' s the opposite of " tailscale
down " ) .
If flags are specified , the flags must be the complete set of desired
settings . An error is returned if any setting would be changed as a
result of an unspecified flag ' s default value , unless the -- reset
flag is also used .
2020-07-15 07:56:48 -07:00
` ) ,
2021-04-10 21:56:18 -07:00
FlagSet : upFlagSet ,
Exec : runUp ,
2020-07-15 07:56:48 -07:00
}
2021-05-03 09:23:01 -07:00
var upFlagSet = newUpFlagSet ( runtime . GOOS , & upArgs )
func newUpFlagSet ( goos string , upArgs * upArgsT ) * flag . FlagSet {
2021-04-10 21:56:18 -07:00
upf := flag . NewFlagSet ( "up" , flag . ExitOnError )
upf . BoolVar ( & upArgs . forceReauth , "force-reauth" , false , "force reauthentication" )
upf . BoolVar ( & upArgs . reset , "reset" , false , "reset unspecified settings to their default values" )
2021-04-18 07:48:53 -07:00
upf . StringVar ( & upArgs . server , "login-server" , ipn . DefaultControlURL , "base URL of control server" )
2021-04-10 21:56:18 -07:00
upf . BoolVar ( & upArgs . acceptRoutes , "accept-routes" , false , "accept routes advertised by other Tailscale nodes" )
upf . BoolVar ( & upArgs . acceptDNS , "accept-dns" , true , "accept DNS configuration from the admin panel" )
upf . BoolVar ( & upArgs . singleRoutes , "host-routes" , true , "install host routes to other Tailscale nodes" )
upf . StringVar ( & upArgs . exitNodeIP , "exit-node" , "" , "Tailscale IP of the exit node for internet traffic" )
2021-04-08 15:56:51 -07:00
upf . BoolVar ( & upArgs . exitNodeAllowLANAccess , "exit-node-allow-lan-access" , false , "Allow direct access to the local network when routing traffic via an exit node" )
2021-04-10 21:56:18 -07:00
upf . BoolVar ( & upArgs . shieldsUp , "shields-up" , false , "don't allow incoming connections" )
upf . StringVar ( & upArgs . advertiseTags , "advertise-tags" , "" , "comma-separated ACL tags to request; each must start with \"tag:\" (e.g. \"tag:eng,tag:montreal,tag:ssh\")" )
upf . StringVar ( & upArgs . authKey , "authkey" , "" , "node authorization key" )
upf . StringVar ( & upArgs . hostname , "hostname" , "" , "hostname to use instead of the one provided by the OS" )
upf . StringVar ( & upArgs . advertiseRoutes , "advertise-routes" , "" , "routes to advertise to other nodes (comma-separated, e.g. \"10.0.0.0/8,192.168.0.0/24\")" )
upf . BoolVar ( & upArgs . advertiseDefaultRoute , "advertise-exit-node" , false , "offer to be an exit node for internet traffic for the tailnet" )
2021-05-03 09:23:01 -07:00
if safesocket . GOOSUsesPeerCreds ( goos ) {
2021-04-16 21:01:29 -07:00
upf . StringVar ( & upArgs . opUser , "operator" , "" , "Unix username to allow to operate on tailscaled without sudo" )
}
2021-05-03 09:23:01 -07:00
switch goos {
case "linux" :
2021-04-10 21:56:18 -07:00
upf . BoolVar ( & upArgs . snat , "snat-subnet-routes" , true , "source NAT traffic to local routes advertised with --advertise-routes" )
upf . StringVar ( & upArgs . netfilterMode , "netfilter-mode" , defaultNetfilterMode ( ) , "netfilter mode (one of on, nodivert, off)" )
2021-05-03 09:23:01 -07:00
case "windows" :
2021-04-13 11:40:30 -07:00
upf . BoolVar ( & upArgs . forceDaemon , "unattended" , false , "run in \"Unattended Mode\" where Tailscale keeps running even after the current GUI user logs out (Windows-only)" )
}
2021-04-10 21:56:18 -07:00
return upf
2021-05-03 09:23:01 -07:00
}
2021-04-10 21:56:18 -07:00
2020-09-10 19:55:09 -07:00
func defaultNetfilterMode ( ) string {
if distro . Get ( ) == distro . Synology {
return "off"
}
return "on"
}
2021-04-17 20:35:20 -07:00
type upArgsT struct {
2021-04-08 15:56:51 -07:00
reset bool
server string
acceptRoutes bool
acceptDNS bool
singleRoutes bool
exitNodeIP string
exitNodeAllowLANAccess bool
shieldsUp bool
forceReauth bool
2021-04-13 11:40:30 -07:00
forceDaemon bool
2021-04-08 15:56:51 -07:00
advertiseRoutes string
advertiseDefaultRoute bool
advertiseTags string
snat bool
netfilterMode string
authKey string
hostname string
2021-04-16 21:01:29 -07:00
opUser string
2020-07-15 15:50:02 -04:00
}
2021-04-17 20:35:20 -07:00
var upArgs upArgsT
2020-08-10 08:10:15 -07:00
func warnf ( format string , args ... interface { } ) {
2020-07-15 07:56:48 -07:00
fmt . Printf ( "Warning: " + format + "\n" , args ... )
}
2021-01-19 16:49:06 -08:00
var (
ipv4default = netaddr . MustParseIPPrefix ( "0.0.0.0/0" )
ipv6default = netaddr . MustParseIPPrefix ( "::/0" )
)
2021-04-17 20:35:20 -07:00
// prefsFromUpArgs returns the ipn.Prefs for the provided args.
//
// Note that the parameters upArgs and warnf are named intentionally
// to shadow the globals to prevent accidental misuse of them. This
// function exists for testing and should have no side effects or
// outside interactions (e.g. no making Tailscale local API calls).
func prefsFromUpArgs ( upArgs upArgsT , warnf logger . Logf , st * ipnstate . Status , goos string ) ( * ipn . Prefs , error ) {
2021-02-24 11:33:03 -08:00
routeMap := map [ netaddr . IPPrefix ] bool { }
2021-01-19 16:49:06 -08:00
var default4 , default6 bool
2020-07-15 07:56:48 -07:00
if upArgs . advertiseRoutes != "" {
advroutes := strings . Split ( upArgs . advertiseRoutes , "," )
for _ , s := range advroutes {
2020-12-24 12:33:55 -08:00
ipp , err := netaddr . ParseIPPrefix ( s )
if err != nil {
2021-04-17 20:35:20 -07:00
return nil , fmt . Errorf ( "%q is not a valid IP address or CIDR prefix" , s )
2020-08-10 08:10:15 -07:00
}
if ipp != ipp . Masked ( ) {
2021-04-17 20:35:20 -07:00
return nil , fmt . Errorf ( "%s has non-address bits set; expected %s" , ipp , ipp . Masked ( ) )
2020-07-15 07:56:48 -07:00
}
2021-01-19 16:49:06 -08:00
if ipp == ipv4default {
default4 = true
} else if ipp == ipv6default {
default6 = true
}
2021-02-24 11:33:03 -08:00
routeMap [ ipp ] = true
2020-07-15 07:56:48 -07:00
}
2021-01-19 16:49:06 -08:00
if default4 && ! default6 {
2021-04-17 20:35:20 -07:00
return nil , fmt . Errorf ( "%s advertised without its IPv6 counterpart, please also advertise %s" , ipv4default , ipv6default )
2021-01-19 16:49:06 -08:00
} else if default6 && ! default4 {
2021-04-17 20:35:20 -07:00
return nil , fmt . Errorf ( "%s advertised without its IPv6 counterpart, please also advertise %s" , ipv6default , ipv4default )
2021-01-19 16:49:06 -08:00
}
2021-02-24 11:33:03 -08:00
}
if upArgs . advertiseDefaultRoute {
routeMap [ netaddr . MustParseIPPrefix ( "0.0.0.0/0" ) ] = true
routeMap [ netaddr . MustParseIPPrefix ( "::/0" ) ] = true
}
routes := make ( [ ] netaddr . IPPrefix , 0 , len ( routeMap ) )
for r := range routeMap {
routes = append ( routes , r )
}
sort . Slice ( routes , func ( i , j int ) bool {
2021-05-14 18:07:28 -07:00
if routes [ i ] . Bits ( ) != routes [ j ] . Bits ( ) {
return routes [ i ] . Bits ( ) < routes [ j ] . Bits ( )
2021-02-24 11:33:03 -08:00
}
2021-05-14 18:07:28 -07:00
return routes [ i ] . IP ( ) . Less ( routes [ j ] . IP ( ) )
2021-02-24 11:33:03 -08:00
} )
2020-07-15 07:56:48 -07:00
2021-01-20 17:24:16 -08:00
var exitNodeIP netaddr . IP
if upArgs . exitNodeIP != "" {
var err error
exitNodeIP , err = netaddr . ParseIP ( upArgs . exitNodeIP )
if err != nil {
2021-04-17 20:35:20 -07:00
return nil , fmt . Errorf ( "invalid IP address %q for --exit-node: %v" , upArgs . exitNodeIP , err )
2021-01-20 17:24:16 -08:00
}
2021-04-08 15:56:51 -07:00
} else if upArgs . exitNodeAllowLANAccess {
2021-04-17 20:35:20 -07:00
return nil , fmt . Errorf ( "--exit-node-allow-lan-access can only be used with --exit-node" )
2021-01-20 17:24:16 -08:00
}
2021-04-17 20:35:20 -07:00
if upArgs . exitNodeIP != "" {
2021-04-10 21:56:18 -07:00
for _ , ip := range st . TailscaleIPs {
if exitNodeIP == ip {
2021-04-17 20:35:20 -07:00
return nil , fmt . Errorf ( "cannot use %s as the exit node as it is a local IP address to this machine, did you mean --advertise-exit-node?" , upArgs . exitNodeIP )
2021-04-10 21:56:18 -07:00
}
}
}
2020-07-15 07:56:48 -07:00
var tags [ ] string
if upArgs . advertiseTags != "" {
tags = strings . Split ( upArgs . advertiseTags , "," )
2020-11-10 22:26:23 -05:00
for _ , tag := range tags {
err := tailcfg . CheckTag ( tag )
if err != nil {
2021-04-17 20:35:20 -07:00
return nil , fmt . Errorf ( "tag: %q: %s" , tag , err )
2020-11-04 08:04:00 -08:00
}
2020-07-15 07:56:48 -07:00
}
}
2020-07-15 18:05:08 -04:00
if len ( upArgs . hostname ) > 256 {
2021-04-17 20:35:20 -07:00
return nil , fmt . Errorf ( "hostname too long: %d bytes (max 256)" , len ( upArgs . hostname ) )
2020-07-15 15:50:02 -04:00
}
2020-07-15 07:56:48 -07:00
prefs := ipn . NewPrefs ( )
prefs . ControlURL = upArgs . server
prefs . WantRunning = true
prefs . RouteAll = upArgs . acceptRoutes
2021-01-20 17:24:16 -08:00
prefs . ExitNodeIP = exitNodeIP
2021-04-08 15:56:51 -07:00
prefs . ExitNodeAllowLANAccess = upArgs . exitNodeAllowLANAccess
2020-07-23 16:09:33 -04:00
prefs . CorpDNS = upArgs . acceptDNS
2020-07-15 07:56:48 -07:00
prefs . AllowSingleHosts = upArgs . singleRoutes
prefs . ShieldsUp = upArgs . shieldsUp
prefs . AdvertiseRoutes = routes
prefs . AdvertiseTags = tags
2020-07-15 15:50:02 -04:00
prefs . Hostname = upArgs . hostname
2021-04-13 11:40:30 -07:00
prefs . ForceDaemon = upArgs . forceDaemon
2021-04-16 21:01:29 -07:00
prefs . OperatorUser = upArgs . opUser
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 14:28:21 -07:00
2021-04-17 20:35:20 -07:00
if goos == "linux" {
2021-05-02 15:35:55 -07:00
prefs . NoSNAT = ! upArgs . snat
2020-07-15 07:56:48 -07:00
switch upArgs . netfilterMode {
case "on" :
2021-02-04 13:12:42 -08:00
prefs . NetfilterMode = preftype . NetfilterOn
2020-07-15 07:56:48 -07:00
case "nodivert" :
2021-02-04 13:12:42 -08:00
prefs . NetfilterMode = preftype . NetfilterNoDivert
2020-08-10 08:10:15 -07:00
warnf ( "netfilter=nodivert; add iptables calls to ts-* chains manually." )
2020-07-15 07:56:48 -07:00
case "off" :
2021-02-04 13:12:42 -08:00
prefs . NetfilterMode = preftype . NetfilterOff
2020-08-10 08:10:15 -07:00
warnf ( "netfilter=off; configure iptables yourself." )
2020-07-15 07:56:48 -07:00
default :
2021-04-17 20:35:20 -07:00
return nil , fmt . Errorf ( "invalid value --netfilter-mode=%q" , upArgs . netfilterMode )
}
}
return prefs , nil
}
func runUp ( ctx context . Context , args [ ] string ) error {
if len ( args ) > 0 {
fatalf ( "too many non-flag arguments: %q" , args )
}
st , err := tailscale . Status ( ctx )
if err != nil {
fatalf ( "can't fetch status from tailscaled: %v" , err )
}
2021-04-21 22:42:01 -07:00
origAuthURL := st . AuthURL
// printAuthURL reports whether we should print out the
// provided auth URL from an IPN notify.
printAuthURL := func ( url string ) bool {
if upArgs . authKey != "" {
// Issue 1755: when using an authkey, don't
// show an authURL that might still be pending
// from a previous non-completed interactive
// login.
return false
}
if upArgs . forceReauth && url == origAuthURL {
return false
}
return true
}
2021-04-17 20:35:20 -07:00
if distro . Get ( ) == distro . Synology {
notSupported := "not yet supported on Synology; see https://github.com/tailscale/tailscale/issues/451"
if upArgs . acceptRoutes {
return errors . New ( "--accept-routes is " + notSupported )
}
if upArgs . exitNodeIP != "" {
return errors . New ( "--exit-node is " + notSupported )
}
if upArgs . netfilterMode != "off" {
return errors . New ( "--netfilter-mode values besides \"off\" " + notSupported )
}
}
prefs , err := prefsFromUpArgs ( upArgs , warnf , st , runtime . GOOS )
if err != nil {
fatalf ( "%s" , err )
}
if len ( prefs . AdvertiseRoutes ) > 0 {
if err := tailscale . CheckIPForwarding ( context . Background ( ) ) ; err != nil {
warnf ( "%v" , err )
2020-07-15 07:56:48 -07:00
}
}
2021-04-10 21:56:18 -07:00
curPrefs , err := tailscale . GetPrefs ( ctx )
if err != nil {
return err
}
2020-07-15 07:56:48 -07:00
2021-04-10 21:56:18 -07:00
if ! upArgs . reset {
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
applyImplicitPrefs ( prefs , curPrefs , os . Getenv ( "USER" ) )
if err := checkForAccidentalSettingReverts ( upFlagSet , curPrefs , prefs , upCheckEnv {
goos : runtime . GOOS ,
curExitNodeIP : exitNodeIP ( prefs , st ) ,
} ) ; err != nil {
2021-04-10 21:56:18 -07:00
fatalf ( "%s" , err )
2021-03-15 15:44:56 -04:00
}
}
2021-04-10 21:56:18 -07:00
controlURLChanged := curPrefs . ControlURL != prefs . ControlURL
if controlURLChanged && st . BackendState == ipn . Running . String ( ) && ! upArgs . forceReauth {
fatalf ( "can't change --login-server without --force-reauth" )
}
// If we're already running and none of the flags require a
// restart, we can just do an EditPrefs call and change the
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
// prefs at runtime (e.g. changing hostname, changing
2021-04-10 21:56:18 -07:00
// advertised tags, routes, etc)
justEdit := st . BackendState == ipn . Running . String ( ) &&
! upArgs . forceReauth &&
! upArgs . reset &&
upArgs . authKey == "" &&
! controlURLChanged
if justEdit {
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
mp := new ( ipn . MaskedPrefs )
mp . WantRunningSet = true
mp . Prefs = * prefs
upFlagSet . Visit ( func ( f * flag . Flag ) {
updateMaskedPrefsFromUpFlag ( mp , f . Name )
} )
2021-04-10 21:56:18 -07:00
_ , err := tailscale . EditPrefs ( ctx , mp )
return err
}
// simpleUp is whether we're running a simple "tailscale up"
// to transition to running from a previously-logged-in but
// down state, without changing any settings.
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
simpleUp := upFlagSet . NFlag ( ) == 0 &&
2021-04-26 11:38:56 -07:00
curPrefs . Persist != nil &&
curPrefs . Persist . LoginName != "" &&
st . BackendState != ipn . NeedsLogin . String ( )
2021-04-10 21:56:18 -07:00
// At this point we need to subscribe to the IPN bus to watch
// for state transitions and possible need to authenticate.
c , bc , pumpCtx , cancel := connect ( ctx )
defer cancel ( )
startingOrRunning := make ( chan bool , 1 ) // gets value once starting or running
gotEngineUpdate := make ( chan bool , 1 ) // gets value upon an engine update
2021-05-04 07:49:29 -07:00
pumpErr := make ( chan error , 1 )
go func ( ) { pumpErr <- pump ( pumpCtx , bc , c ) } ( )
2021-04-10 21:56:18 -07:00
printed := ! simpleUp
2020-08-28 20:49:14 +08:00
var loginOnce sync . Once
startLoginInteractive := func ( ) { loginOnce . Do ( func ( ) { bc . StartLoginInteractive ( ) } ) }
2020-07-15 07:56:48 -07:00
2021-04-06 22:11:50 -07:00
bc . SetNotifyCallback ( func ( n ipn . Notify ) {
2021-04-10 21:56:18 -07:00
if n . Engine != nil {
select {
case gotEngineUpdate <- true :
default :
}
}
2021-04-06 22:11:50 -07:00
if n . ErrMessage != nil {
msg := * n . ErrMessage
if msg == ipn . ErrMsgPermissionDenied {
switch runtime . GOOS {
case "windows" :
msg += " (Tailscale service in use by other user?)"
default :
msg += " (try 'sudo tailscale up [...]')"
2021-02-01 13:52:01 -08:00
}
2020-07-15 07:56:48 -07:00
}
2021-04-06 22:11:50 -07:00
fatalf ( "backend error: %v\n" , msg )
}
if s := n . State ; s != nil {
switch * s {
case ipn . NeedsLogin :
printed = true
startLoginInteractive ( )
case ipn . NeedsMachineAuth :
printed = true
fmt . Fprintf ( os . Stderr , "\nTo authorize your machine, visit (as admin):\n\n\t%s/admin/machines\n\n" , upArgs . server )
case ipn . Starting , ipn . Running :
// Done full authentication process
if printed {
// Only need to print an update if we printed the "please click" message earlier.
fmt . Fprintf ( os . Stderr , "Success.\n" )
2020-07-15 07:56:48 -07:00
}
2021-04-10 21:56:18 -07:00
select {
case startingOrRunning <- true :
default :
}
2021-04-06 22:11:50 -07:00
cancel ( )
2020-07-15 07:56:48 -07:00
}
2021-04-06 22:11:50 -07:00
}
2021-04-21 22:42:01 -07:00
if url := n . BrowseToURL ; url != nil && printAuthURL ( * url ) {
2021-04-10 21:56:18 -07:00
printed = true
2021-04-06 22:11:50 -07:00
fmt . Fprintf ( os . Stderr , "\nTo authenticate, visit:\n\n\t%s\n\n" , * url )
}
} )
2021-04-10 21:56:18 -07:00
// Wait for backend client to be connected so we know
// we're subscribed to updates. Otherwise we can miss
// an update upon its transition to running. Do so by causing some traffic
// back to the bus that we then wait on.
bc . RequestEngineStatus ( )
2021-04-16 10:27:56 -07:00
select {
case <- gotEngineUpdate :
case <- pumpCtx . Done ( ) :
return pumpCtx . Err ( )
2021-05-04 07:49:29 -07:00
case err := <- pumpErr :
return err
2021-04-16 10:27:56 -07:00
}
2021-04-06 22:11:50 -07:00
2021-04-10 21:56:18 -07:00
// Special case: bare "tailscale up" means to just start
// running, if there's ever been a login.
if simpleUp {
_ , err := tailscale . EditPrefs ( ctx , & ipn . MaskedPrefs {
Prefs : ipn . Prefs {
WantRunning : true ,
} ,
WantRunningSet : true ,
} )
if err != nil {
return err
}
} else {
opts := ipn . Options {
2021-05-04 13:04:00 -07:00
StateKey : ipn . GlobalDaemonStateKey ,
AuthKey : upArgs . authKey ,
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 04:26:07 -04:00
UpdatePrefs : prefs ,
2021-04-10 21:56:18 -07:00
}
// On Windows, we still run in mostly the "legacy" way that
// predated the server's StateStore. That is, we send an empty
// StateKey and send the prefs directly. Although the Windows
// supports server mode, though, the transition to StateStore
// is only half complete. Only server mode uses it, and the
// Windows service (~tailscaled) is the one that computes the
// StateKey based on the connection identity. So for now, just
// do as the Windows GUI's always done:
if runtime . GOOS == "windows" {
// The Windows service will set this as needed based
// on our connection's identity.
opts . StateKey = ""
opts . Prefs = prefs
}
bc . Start ( opts )
2021-04-22 15:04:53 -07:00
if upArgs . forceReauth {
startLoginInteractive ( )
}
2020-07-15 07:56:48 -07:00
}
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 14:28:21 -07:00
2021-04-10 21:56:18 -07:00
select {
case <- startingOrRunning :
return nil
2021-04-16 10:27:56 -07:00
case <- pumpCtx . Done ( ) :
2021-04-10 21:56:18 -07:00
select {
case <- startingOrRunning :
return nil
default :
}
2021-04-16 10:27:56 -07:00
return pumpCtx . Err ( )
2021-05-04 07:49:29 -07:00
case err := <- pumpErr :
return err
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 14:28:21 -07:00
}
2021-04-10 21:56:18 -07:00
}
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 14:28:21 -07:00
2021-04-10 21:56:18 -07:00
var (
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
prefsOfFlag = map [ string ] [ ] string { } // "exit-node" => ExitNodeIP, ExitNodeID
2021-04-10 21:56:18 -07:00
)
func init ( ) {
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
// Both these have the same ipn.Pref:
addPrefFlagMapping ( "advertise-exit-node" , "AdvertiseRoutes" )
addPrefFlagMapping ( "advertise-routes" , "AdvertiseRoutes" )
// And this flag has two ipn.Prefs:
addPrefFlagMapping ( "exit-node" , "ExitNodeIP" , "ExitNodeID" )
// The rest are 1:1:
2021-04-10 21:56:18 -07:00
addPrefFlagMapping ( "accept-dns" , "CorpDNS" )
addPrefFlagMapping ( "accept-routes" , "RouteAll" )
addPrefFlagMapping ( "advertise-tags" , "AdvertiseTags" )
addPrefFlagMapping ( "host-routes" , "AllowSingleHosts" )
addPrefFlagMapping ( "hostname" , "Hostname" )
addPrefFlagMapping ( "login-server" , "ControlURL" )
addPrefFlagMapping ( "netfilter-mode" , "NetfilterMode" )
addPrefFlagMapping ( "shields-up" , "ShieldsUp" )
addPrefFlagMapping ( "snat-subnet-routes" , "NoSNAT" )
2021-04-08 15:56:51 -07:00
addPrefFlagMapping ( "exit-node-allow-lan-access" , "ExitNodeAllowLANAccess" )
2021-04-13 11:40:30 -07:00
addPrefFlagMapping ( "unattended" , "ForceDaemon" )
2021-04-16 21:01:29 -07:00
addPrefFlagMapping ( "operator" , "OperatorUser" )
2021-04-10 21:56:18 -07:00
}
func addPrefFlagMapping ( flagName string , prefNames ... string ) {
prefsOfFlag [ flagName ] = prefNames
2021-04-13 11:40:30 -07:00
prefType := reflect . TypeOf ( ipn . Prefs { } )
2021-04-10 21:56:18 -07:00
for _ , pref := range prefNames {
2021-04-13 11:40:30 -07:00
// Crash at runtime if there's a typo in the prefName.
if _ , ok := prefType . FieldByName ( pref ) ; ! ok {
panic ( fmt . Sprintf ( "invalid ipn.Prefs field %q" , pref ) )
}
2021-04-10 21:56:18 -07:00
}
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
// preflessFlag reports whether flagName is a flag that doesn't
// correspond to an ipn.Pref.
func preflessFlag ( flagName string ) bool {
switch flagName {
case "authkey" , "force-reauth" , "reset" :
return true
}
return false
2021-05-06 15:27:02 -07:00
}
2021-04-10 21:56:18 -07:00
func updateMaskedPrefsFromUpFlag ( mp * ipn . MaskedPrefs , flagName string ) {
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
if preflessFlag ( flagName ) {
return
}
2021-04-10 21:56:18 -07:00
if prefs , ok := prefsOfFlag [ flagName ] ; ok {
for _ , pref := range prefs {
reflect . ValueOf ( mp ) . Elem ( ) . FieldByName ( pref + "Set" ) . SetBool ( true )
}
return
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
panic ( fmt . Sprintf ( "internal error: unhandled flag %q" , flagName ) )
2021-04-10 21:56:18 -07:00
}
2020-07-15 07:56:48 -07:00
2021-04-26 14:39:49 -07:00
const accidentalUpPrefix = "Error: changing settings via 'tailscale up' requires mentioning all\n" +
"non-default flags. To proceed, either re-run your command with --reset or\n" +
2021-04-27 10:16:08 -04:00
"use the command below to explicitly mention the current value of\n" +
2021-04-26 14:39:49 -07:00
"all non-default settings:\n\n" +
"\ttailscale up"
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
// upCheckEnv are extra parameters describing the environment as
// needed by checkForAccidentalSettingReverts and friends.
type upCheckEnv struct {
goos string
curExitNodeIP netaddr . IP
}
// checkForAccidentalSettingReverts (the "up checker") checks for
// people running "tailscale up" with a subset of the flags they
// originally ran it with.
2021-04-10 21:56:18 -07:00
//
// For example, in Tailscale 1.6 and prior, a user might've advertised
// a tag, but later tried to change just one other setting and forgot
// to mention the tag later and silently wiped it out. We now
// require --reset to change preferences to flag default values when
// the flag is not mentioned on the command line.
//
// curPrefs is what's currently active on the server.
//
// mp is the mask of settings actually set, where mp.Prefs is the new
// preferences to set, including any values set from implicit flags.
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
func checkForAccidentalSettingReverts ( flagSet * flag . FlagSet , curPrefs , newPrefs * ipn . Prefs , env upCheckEnv ) error {
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-17 22:50:58 -07:00
if curPrefs . ControlURL == "" {
// Don't validate things on initial "up" before a control URL has been set.
return nil
}
2021-04-10 21:56:18 -07:00
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
flagIsSet := map [ string ] bool { }
flagSet . Visit ( func ( f * flag . Flag ) {
flagIsSet [ f . Name ] = true
} )
2021-04-10 21:56:18 -07:00
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
if len ( flagIsSet ) == 0 {
// A bare "tailscale up" is a special case to just
// mean bringing the network up without any changes.
return nil
}
// flagsCur is what flags we'd need to use to keep the exact
// settings as-is.
flagsCur := prefsToFlags ( env , curPrefs )
flagsNew := prefsToFlags ( env , newPrefs )
2021-04-26 14:39:49 -07:00
var missing [ ] string
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
for flagName := range flagsCur {
valCur , valNew := flagsCur [ flagName ] , flagsNew [ flagName ]
if flagIsSet [ flagName ] {
2021-04-30 15:36:52 -04:00
continue
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
if reflect . DeepEqual ( valCur , valNew ) {
2021-04-10 21:56:18 -07:00
continue
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
missing = append ( missing , fmtFlagValueArg ( flagName , valCur ) )
}
if len ( missing ) == 0 {
return nil
}
sort . Strings ( missing )
2021-05-05 20:17:23 -07:00
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
// Compute the stringification of the explicitly provided args in flagSet
// to prepend to the command to run.
var explicit [ ] string
flagSet . Visit ( func ( f * flag . Flag ) {
type isBool interface {
IsBoolFlag ( ) bool
2021-05-05 20:17:23 -07:00
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
if ib , ok := f . Value . ( isBool ) ; ok && ib . IsBoolFlag ( ) {
if f . Value . String ( ) == "false" {
explicit = append ( explicit , "--" + f . Name + "=false" )
} else {
explicit = append ( explicit , "--" + f . Name )
2021-04-17 19:14:59 -07:00
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
} else {
explicit = append ( explicit , fmtFlagValueArg ( f . Name , f . Value . String ( ) ) )
2021-04-17 19:14:59 -07:00
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
} )
2021-04-21 21:41:13 -07:00
2021-04-26 14:39:49 -07:00
var sb strings . Builder
sb . WriteString ( accidentalUpPrefix )
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
for _ , a := range append ( explicit , missing ... ) {
2021-04-26 14:39:49 -07:00
fmt . Fprintf ( & sb , " %s" , a )
}
sb . WriteString ( "\n\n" )
return errors . New ( sb . String ( ) )
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
// applyImplicitPrefs mutates prefs to add implicit preferences. Currently
// this is just the operator user, which only needs to be set if it doesn't
// match the current user.
//
// curUser is os.Getenv("USER"). It's pulled out for testability.
func applyImplicitPrefs ( prefs , oldPrefs * ipn . Prefs , curUser string ) {
if prefs . OperatorUser == "" && oldPrefs . OperatorUser == curUser {
prefs . OperatorUser = oldPrefs . OperatorUser
2021-04-26 14:39:49 -07:00
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
}
func flagAppliesToOS ( flag , goos string ) bool {
switch flag {
case "netfilter-mode" , "snat-subnet-routes" :
return goos == "linux"
case "unattended" :
return goos == "windows"
2021-04-26 14:39:49 -07:00
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
return true
2021-04-10 21:56:18 -07:00
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
func prefsToFlags ( env upCheckEnv , prefs * ipn . Prefs ) ( flagVal map [ string ] interface { } ) {
ret := make ( map [ string ] interface { } )
exitNodeIPStr := func ( ) string {
if ! prefs . ExitNodeIP . IsZero ( ) {
return prefs . ExitNodeIP . String ( )
}
if prefs . ExitNodeID . IsZero ( ) || env . curExitNodeIP . IsZero ( ) {
return ""
}
return env . curExitNodeIP . String ( )
}
fs := newUpFlagSet ( env . goos , new ( upArgsT ) /* dummy */ )
fs . VisitAll ( func ( f * flag . Flag ) {
if preflessFlag ( f . Name ) {
return
}
set := func ( v interface { } ) {
if flagAppliesToOS ( f . Name , env . goos ) {
ret [ f . Name ] = v
} else {
ret [ f . Name ] = nil
2021-04-26 14:39:49 -07:00
}
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
switch f . Name {
default :
panic ( fmt . Sprintf ( "unhandled flag %q" , f . Name ) )
case "login-server" :
set ( prefs . ControlURL )
case "accept-routes" :
set ( prefs . RouteAll )
case "host-routes" :
set ( prefs . AllowSingleHosts )
case "accept-dns" :
set ( prefs . CorpDNS )
case "shields-up" :
set ( prefs . ShieldsUp )
case "exit-node" :
set ( exitNodeIPStr ( ) )
case "exit-node-allow-lan-access" :
set ( prefs . ExitNodeAllowLANAccess )
case "advertise-tags" :
set ( strings . Join ( prefs . AdvertiseTags , "," ) )
case "hostname" :
set ( prefs . Hostname )
case "operator" :
set ( prefs . OperatorUser )
case "advertise-routes" :
var sb strings . Builder
for i , r := range withoutExitNodes ( prefs . AdvertiseRoutes ) {
if i > 0 {
sb . WriteByte ( ',' )
}
sb . WriteString ( r . String ( ) )
}
set ( sb . String ( ) )
case "advertise-exit-node" :
set ( hasExitNodeRoutes ( prefs . AdvertiseRoutes ) )
case "snat-subnet-routes" :
set ( ! prefs . NoSNAT )
case "netfilter-mode" :
set ( prefs . NetfilterMode . String ( ) )
case "unattended" :
set ( prefs . ForceDaemon )
}
} )
return ret
}
func fmtFlagValueArg ( flagName string , val interface { } ) string {
if val == true {
return "--" + flagName
2021-04-10 21:56:18 -07:00
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
if val == "" {
return "--" + flagName + "="
}
return fmt . Sprintf ( "--%s=%v" , flagName , shellquote . Join ( fmt . Sprint ( val ) ) )
2020-07-15 07:56:48 -07:00
}
2021-04-21 21:41:13 -07:00
func hasExitNodeRoutes ( rr [ ] netaddr . IPPrefix ) bool {
var v4 , v6 bool
for _ , r := range rr {
2021-05-14 18:07:28 -07:00
if r . Bits ( ) == 0 {
if r . IP ( ) . Is4 ( ) {
2021-04-21 21:41:13 -07:00
v4 = true
2021-05-14 18:07:28 -07:00
} else if r . IP ( ) . Is6 ( ) {
2021-04-21 21:41:13 -07:00
v6 = true
}
}
}
return v4 && v6
}
2021-05-05 20:17:23 -07:00
// withoutExitNodes returns rr unchanged if it has only 1 or 0 /0
// routes. If it has both IPv4 and IPv6 /0 routes, then it returns
// a copy with all /0 routes removed.
func withoutExitNodes ( rr [ ] netaddr . IPPrefix ) [ ] netaddr . IPPrefix {
if ! hasExitNodeRoutes ( rr ) {
return rr
}
var out [ ] netaddr . IPPrefix
for _ , r := range rr {
2021-05-14 18:07:28 -07:00
if r . Bits ( ) > 0 {
2021-05-05 20:17:23 -07:00
out = append ( out , r )
}
}
return out
}
cmd/tailscale: rewrite the "up" checker, fix bugs
The old way was way too fragile and had felt like it had more special
cases than normal cases. (see #1874, #1860, #1834, etc) It became very
obvious the old algorithm didn't work when we made the output be
pretty and try to show the user the command they need to run in
5ecc7c7200bda43f02f9a04fb684ad4f3614c48a for #1746)
The new algorithm is to map the prefs (current and new) back to flags
and then compare flags. This nicely handles the OS-specific flags and
the n:1 and 1:n flag:pref cases.
No change in the existing already-massive test suite, except some ordering
differences (the missing items are now sorted), but some new tests are
added for behavior that was broken before. In particular, it now:
* preserves non-pref boolean flags set to false, and preserves exit
node IPs (mapping them back from the ExitNodeID pref, as well as
ExitNodeIP),
* doesn't ignore --advertise-exit-node when doing an EditPrefs call
(#1880)
* doesn't lose the --operator on the non-EditPrefs paths (e.g. with
--force-reauth, or when the backend was not in state Running).
Fixes #1880
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-06 21:25:16 -07:00
// exitNodeIP returns the exit node IP from p, using st to map
// it from its ID form to an IP address if needed.
func exitNodeIP ( p * ipn . Prefs , st * ipnstate . Status ) ( ip netaddr . IP ) {
if p == nil {
return
}
if ! p . ExitNodeIP . IsZero ( ) {
return p . ExitNodeIP
}
id := p . ExitNodeID
if id . IsZero ( ) {
return
}
for _ , p := range st . Peer {
if p . ID == id {
if len ( p . TailscaleIPs ) > 0 {
return p . TailscaleIPs [ 0 ]
}
break
}
}
return
}