diff --git a/cmd/tailscale/cli/set.go b/cmd/tailscale/cli/set.go index 94503082f..126105669 100644 --- a/cmd/tailscale/cli/set.go +++ b/cmd/tailscale/cli/set.go @@ -58,6 +58,9 @@ type setArgsT struct { updateCheck bool updateApply bool postureChecking bool + snat bool + statefulFiltering bool + netfilterMode string } func newSetFlagSet(goos string, setArgs *setArgsT) *flag.FlagSet { @@ -98,6 +101,10 @@ func newSetFlagSet(goos string, setArgs *setArgsT) *flag.FlagSet { setf.StringVar(&setArgs.opUser, "operator", "", "Unix username to allow to operate on tailscaled without sudo") } switch goos { + case "linux": + setf.BoolVar(&setArgs.snat, "snat-subnet-routes", true, "source NAT traffic to local routes advertised with --advertise-routes") + setf.BoolVar(&setArgs.statefulFiltering, "stateful-filtering", true, "apply stateful filtering to forwarded packets (subnet routers, exit nodes, etc.)") + setf.StringVar(&setArgs.netfilterMode, "netfilter-mode", defaultNetfilterMode(), "netfilter mode (one of on, nodivert, off)") 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)") } @@ -121,6 +128,9 @@ func runSet(ctx context.Context, args []string) (retErr error) { return err } + // Note that even though we set the values here regardless of whether the + // user passed the flag, the value is only used if the user passed the flag. + // See updateMaskedPrefsFromUpOrSetFlag. maskedPrefs := &ipn.MaskedPrefs{ Prefs: ipn.Prefs{ ProfileName: setArgs.profileName, @@ -132,6 +142,7 @@ func runSet(ctx context.Context, args []string) (retErr error) { RunWebClient: setArgs.runWebClient, Hostname: setArgs.hostname, OperatorUser: setArgs.opUser, + NoSNAT: !setArgs.snat, ForceDaemon: setArgs.forceDaemon, AutoUpdate: ipn.AutoUpdatePrefs{ Check: setArgs.updateCheck, @@ -140,10 +151,22 @@ func runSet(ctx context.Context, args []string) (retErr error) { AppConnector: ipn.AppConnectorPrefs{ Advertise: setArgs.advertiseConnector, }, - PostureChecking: setArgs.postureChecking, + PostureChecking: setArgs.postureChecking, + NoStatefulFiltering: opt.NewBool(!setArgs.statefulFiltering), }, } + if effectiveGOOS() == "linux" { + nfMode, warning, err := netfilterModeFromFlag(setArgs.netfilterMode) + if err != nil { + return err + } + if warning != "" { + warnf(warning) + } + maskedPrefs.Prefs.NetfilterMode = nfMode + } + if setArgs.exitNodeIP != "" { if err := maskedPrefs.Prefs.SetExitNodeIP(setArgs.exitNodeIP, st); err != nil { var e ipn.ExitNodeLocalIPError diff --git a/cmd/tailscale/cli/up.go b/cmd/tailscale/cli/up.go index 08b336478..16b28233e 100644 --- a/cmd/tailscale/cli/up.go +++ b/cmd/tailscale/cli/up.go @@ -295,25 +295,42 @@ func prefsFromUpArgs(upArgs upArgsT, warnf logger.Logf, st *ipnstate.Status, goo // Backfills for NoStatefulFiltering occur when loading a profile; just set it explicitly here. prefs.NoStatefulFiltering.Set(!upArgs.statefulFiltering) - - switch upArgs.netfilterMode { - case "on": - prefs.NetfilterMode = preftype.NetfilterOn - case "nodivert": - prefs.NetfilterMode = preftype.NetfilterNoDivert - warnf("netfilter=nodivert; add iptables calls to ts-* chains manually.") - case "off": - prefs.NetfilterMode = preftype.NetfilterOff - if defaultNetfilterMode() != "off" { - warnf("netfilter=off; configure iptables yourself.") - } - default: - return nil, fmt.Errorf("invalid value --netfilter-mode=%q", upArgs.netfilterMode) + v, warning, err := netfilterModeFromFlag(upArgs.netfilterMode) + if err != nil { + return nil, err + } + prefs.NetfilterMode = v + if warning != "" { + warnf(warning) } } return prefs, nil } +// netfilterModeFromFlag returns the preftype.NetfilterMode for the provided +// flag value. It returns a warning if there is something the user should know +// about the value. +func netfilterModeFromFlag(v string) (_ preftype.NetfilterMode, warning string, _ error) { + switch v { + case "on", "nodivert", "off": + default: + return preftype.NetfilterOn, "", fmt.Errorf("invalid value --netfilter-mode=%q", v) + } + m, err := preftype.ParseNetfilterMode(v) + if err != nil { + return preftype.NetfilterOn, "", err + } + switch m { + case preftype.NetfilterNoDivert: + warning = "netfilter=nodivert; add iptables calls to ts-* chains manually." + case preftype.NetfilterOff: + if defaultNetfilterMode() != "off" { + warning = "netfilter=off; configure iptables yourself." + } + } + return m, warning, nil +} + // updatePrefs returns how to edit preferences based on the // flag-provided 'prefs' and the currently active 'curPrefs'. //