2023-01-27 13:37:20 -08:00
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
2022-10-25 18:02:58 -07:00
package cli
import (
"context"
"errors"
"flag"
"fmt"
2022-11-12 14:53:36 +05:00
"net/netip"
2023-10-24 12:17:55 -06:00
"os/exec"
2022-10-25 18:02:58 -07:00
"github.com/peterbourgon/ff/v3/ffcli"
2023-09-01 14:45:12 -06:00
"tailscale.com/clientupdate"
2022-10-25 18:02:58 -07:00
"tailscale.com/ipn"
2023-08-08 16:21:17 -07:00
"tailscale.com/net/netutil"
2022-11-12 14:53:36 +05:00
"tailscale.com/net/tsaddr"
2022-10-25 18:02:58 -07:00
"tailscale.com/safesocket"
2023-08-18 10:57:04 -07:00
"tailscale.com/types/views"
2023-10-24 12:17:55 -06:00
"tailscale.com/version"
2022-10-25 18:02:58 -07:00
)
var setCmd = & ffcli . Command {
Name : "set" ,
ShortUsage : "set [flags]" ,
ShortHelp : "Change specified preferences" ,
LongHelp : ` "tailscale set" allows changing specific preferences .
Unlike "tailscale up" , this command does not require the complete set of desired settings .
Only settings explicitly mentioned will be set . There are no default values . ` ,
FlagSet : setFlagSet ,
Exec : runSet ,
UsageFunc : usageFuncNoDefaultValues ,
}
type setArgsT struct {
acceptRoutes bool
acceptDNS bool
exitNodeIP string
exitNodeAllowLANAccess bool
shieldsUp bool
runSSH bool
2023-11-08 09:39:52 -08:00
runWebClient bool
2022-10-25 18:02:58 -07:00
hostname string
advertiseRoutes string
advertiseDefaultRoute bool
2023-10-26 15:55:32 -07:00
advertiseConnector bool
2022-10-25 18:02:58 -07:00
opUser string
acceptedRisks string
2022-11-18 14:36:45 +05:00
profileName string
2022-11-29 21:08:44 -08:00
forceDaemon bool
2023-09-01 14:45:12 -06:00
updateCheck bool
updateApply bool
2023-10-03 11:49:31 +02:00
postureChecking bool
2022-10-25 18:02:58 -07:00
}
func newSetFlagSet ( goos string , setArgs * setArgsT ) * flag . FlagSet {
setf := newFlagSet ( "set" )
2022-12-01 16:29:44 -08:00
setf . StringVar ( & setArgs . profileName , "nickname" , "" , "nickname for the current account" )
2022-10-25 18:02:58 -07:00
setf . BoolVar ( & setArgs . acceptRoutes , "accept-routes" , false , "accept routes advertised by other Tailscale nodes" )
setf . BoolVar ( & setArgs . acceptDNS , "accept-dns" , false , "accept DNS configuration from the admin panel" )
setf . StringVar ( & setArgs . exitNodeIP , "exit-node" , "" , "Tailscale exit node (IP or base name) for internet traffic, or empty string to not use an exit node" )
setf . BoolVar ( & setArgs . exitNodeAllowLANAccess , "exit-node-allow-lan-access" , false , "Allow direct access to the local network when routing traffic via an exit node" )
setf . BoolVar ( & setArgs . shieldsUp , "shields-up" , false , "don't allow incoming connections" )
setf . BoolVar ( & setArgs . runSSH , "ssh" , false , "run an SSH server, permitting access per tailnet admin's declared policy" )
setf . StringVar ( & setArgs . hostname , "hostname" , "" , "hostname to use instead of the one provided by the OS" )
setf . StringVar ( & setArgs . advertiseRoutes , "advertise-routes" , "" , "routes to advertise to other nodes (comma-separated, e.g. \"10.0.0.0/8,192.168.0.0/24\") or empty string to not advertise routes" )
setf . BoolVar ( & setArgs . advertiseDefaultRoute , "advertise-exit-node" , false , "offer to be an exit node for internet traffic for the tailnet" )
2023-10-26 15:55:32 -07:00
setf . BoolVar ( & setArgs . advertiseConnector , "advertise-connector" , false , "offer to be an exit node for internet traffic for the tailnet" )
2023-10-24 12:34:24 -06:00
setf . BoolVar ( & setArgs . updateCheck , "update-check" , true , "notify about available Tailscale updates" )
setf . BoolVar ( & setArgs . updateApply , "auto-update" , false , "automatically update to the latest available version" )
2023-10-03 11:49:31 +02:00
setf . BoolVar ( & setArgs . postureChecking , "posture-checking" , false , "HIDDEN: allow management plane to gather device posture information" )
2023-11-08 09:39:52 -08:00
// TODO(tailscale/corp#14335): during development only expose -webclient on dev and unstable builds
if version . GetMeta ( ) . IsDev || version . IsUnstableBuild ( ) {
setf . BoolVar ( & setArgs . runWebClient , "webclient" , false , "run a web client, permitting access per tailnet admin's declared policy" )
}
2022-10-25 18:02:58 -07:00
if safesocket . GOOSUsesPeerCreds ( goos ) {
setf . StringVar ( & setArgs . opUser , "operator" , "" , "Unix username to allow to operate on tailscaled without sudo" )
}
2022-11-29 21:08:44 -08:00
switch goos {
case "windows" :
setf . BoolVar ( & setArgs . forceDaemon , "unattended" , false , "run in \"Unattended Mode\" where Tailscale keeps running even after the current GUI user logs out (Windows-only)" )
}
2022-10-25 18:02:58 -07:00
registerAcceptRiskFlag ( setf , & setArgs . acceptedRisks )
return setf
}
var (
setArgs setArgsT
setFlagSet = newSetFlagSet ( effectiveGOOS ( ) , & setArgs )
)
func runSet ( ctx context . Context , args [ ] string ) ( retErr error ) {
if len ( args ) > 0 {
fatalf ( "too many non-flag arguments: %q" , args )
}
st , err := localClient . Status ( ctx )
if err != nil {
return err
}
maskedPrefs := & ipn . MaskedPrefs {
Prefs : ipn . Prefs {
2022-11-18 14:36:45 +05:00
ProfileName : setArgs . profileName ,
2022-10-25 18:02:58 -07:00
RouteAll : setArgs . acceptRoutes ,
CorpDNS : setArgs . acceptDNS ,
ExitNodeAllowLANAccess : setArgs . exitNodeAllowLANAccess ,
ShieldsUp : setArgs . shieldsUp ,
RunSSH : setArgs . runSSH ,
2023-11-08 09:39:52 -08:00
RunWebClient : setArgs . runWebClient ,
2022-10-25 18:02:58 -07:00
Hostname : setArgs . hostname ,
OperatorUser : setArgs . opUser ,
2022-11-29 21:08:44 -08:00
ForceDaemon : setArgs . forceDaemon ,
2023-09-01 14:45:12 -06:00
AutoUpdate : ipn . AutoUpdatePrefs {
Check : setArgs . updateCheck ,
Apply : setArgs . updateApply ,
} ,
2023-10-26 15:55:32 -07:00
AppConnector : ipn . AppConnectorPrefs {
Advertise : setArgs . advertiseConnector ,
} ,
2023-10-03 11:49:31 +02:00
PostureChecking : setArgs . postureChecking ,
2022-10-25 18:02:58 -07:00
} ,
}
if setArgs . exitNodeIP != "" {
if err := maskedPrefs . Prefs . SetExitNodeIP ( setArgs . exitNodeIP , st ) ; err != nil {
var e ipn . ExitNodeLocalIPError
if errors . As ( err , & e ) {
return fmt . Errorf ( "%w; did you mean --advertise-exit-node?" , err )
}
return err
}
}
2022-11-12 14:53:36 +05:00
var advertiseExitNodeSet , advertiseRoutesSet bool
2022-10-25 18:02:58 -07:00
setFlagSet . Visit ( func ( f * flag . Flag ) {
updateMaskedPrefsFromUpOrSetFlag ( maskedPrefs , f . Name )
2022-11-12 14:53:36 +05:00
switch f . Name {
case "advertise-exit-node" :
advertiseExitNodeSet = true
case "advertise-routes" :
advertiseRoutesSet = true
}
2022-10-25 18:02:58 -07:00
} )
if maskedPrefs . IsEmpty ( ) {
2022-10-30 14:49:40 -07:00
return flag . ErrHelp
2022-10-25 18:02:58 -07:00
}
2022-11-12 14:53:36 +05:00
curPrefs , err := localClient . GetPrefs ( ctx )
if err != nil {
return err
}
if maskedPrefs . AdvertiseRoutesSet {
maskedPrefs . AdvertiseRoutes , err = calcAdvertiseRoutesForSet ( advertiseExitNodeSet , advertiseRoutesSet , curPrefs , setArgs )
2022-10-25 18:02:58 -07:00
if err != nil {
return err
}
2022-11-12 14:53:36 +05:00
}
2022-10-25 18:02:58 -07:00
2022-11-12 14:53:36 +05:00
if maskedPrefs . RunSSHSet {
2022-10-25 18:02:58 -07:00
wantSSH , haveSSH := maskedPrefs . RunSSH , curPrefs . RunSSH
if err := presentSSHToggleRisk ( wantSSH , haveSSH , setArgs . acceptedRisks ) ; err != nil {
return err
}
}
2023-09-01 14:45:12 -06:00
if maskedPrefs . AutoUpdateSet {
2023-10-24 12:17:55 -06:00
// On macsys, tailscaled will set the Sparkle auto-update setting. It
// does not use clientupdate.
if version . IsMacSysExt ( ) {
apply := "0"
if maskedPrefs . AutoUpdate . Apply {
apply = "1"
}
out , err := exec . Command ( "defaults" , "write" , "io.tailscale.ipn.macsys" , "SUAutomaticallyUpdate" , apply ) . CombinedOutput ( )
if err != nil {
return fmt . Errorf ( "failed to enable automatic updates: %v, %q" , err , out )
}
} else {
_ , err := clientupdate . NewUpdater ( clientupdate . Arguments { ForAutoUpdate : true } )
if errors . Is ( err , errors . ErrUnsupported ) {
return errors . New ( "automatic updates are not supported on this platform" )
}
2023-09-01 14:45:12 -06:00
}
}
2022-11-18 14:36:45 +05:00
checkPrefs := curPrefs . Clone ( )
checkPrefs . ApplyEdits ( maskedPrefs )
if err := localClient . CheckPrefs ( ctx , checkPrefs ) ; err != nil {
return err
}
2022-10-25 18:02:58 -07:00
_ , err = localClient . EditPrefs ( ctx , maskedPrefs )
return err
}
2022-11-12 14:53:36 +05:00
// calcAdvertiseRoutesForSet returns the new value for Prefs.AdvertiseRoutes based on the
// current value, the flags passed to "tailscale set".
// advertiseExitNodeSet is whether the --advertise-exit-node flag was set.
// advertiseRoutesSet is whether the --advertise-routes flag was set.
// curPrefs is the current Prefs.
// setArgs is the parsed command-line arguments.
func calcAdvertiseRoutesForSet ( advertiseExitNodeSet , advertiseRoutesSet bool , curPrefs * ipn . Prefs , setArgs setArgsT ) ( routes [ ] netip . Prefix , err error ) {
if advertiseExitNodeSet && advertiseRoutesSet {
2023-08-08 16:21:17 -07:00
return netutil . CalcAdvertiseRoutes ( setArgs . advertiseRoutes , setArgs . advertiseDefaultRoute )
2022-11-12 14:53:36 +05:00
}
if advertiseRoutesSet {
2023-08-08 16:21:17 -07:00
return netutil . CalcAdvertiseRoutes ( setArgs . advertiseRoutes , curPrefs . AdvertisesExitNode ( ) )
2022-11-12 14:53:36 +05:00
}
if advertiseExitNodeSet {
alreadyAdvertisesExitNode := curPrefs . AdvertisesExitNode ( )
if alreadyAdvertisesExitNode == setArgs . advertiseDefaultRoute {
return curPrefs . AdvertiseRoutes , nil
}
2023-08-18 10:57:04 -07:00
routes = tsaddr . FilterPrefixesCopy ( views . SliceOf ( curPrefs . AdvertiseRoutes ) , func ( p netip . Prefix ) bool {
2022-11-12 14:53:36 +05:00
return p . Bits ( ) != 0
} )
if setArgs . advertiseDefaultRoute {
routes = append ( routes , tsaddr . AllIPv4 ( ) , tsaddr . AllIPv6 ( ) )
}
return routes , nil
}
return nil , nil
}