diff --git a/cmd/tailscale/cli/cli_test.go b/cmd/tailscale/cli/cli_test.go index 9c87ec33f..b0658fd95 100644 --- a/cmd/tailscale/cli/cli_test.go +++ b/cmd/tailscale/cli/cli_test.go @@ -24,6 +24,7 @@ import ( "tailscale.com/tka" "tailscale.com/tstest" "tailscale.com/types/logger" + "tailscale.com/types/opt" "tailscale.com/types/persist" "tailscale.com/types/preftype" "tailscale.com/version/distro" @@ -176,9 +177,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "bare_up_means_up", flags: []string{}, curPrefs: &ipn.Prefs{ - ControlURL: ipn.DefaultControlURL, - WantRunning: false, - Hostname: "foo", + ControlURL: ipn.DefaultControlURL, + WantRunning: false, + Hostname: "foo", + NoStatefulFiltering: opt.NewBool(true), }, want: "", }, @@ -186,11 +188,12 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "losing_hostname", flags: []string{"--accept-dns"}, curPrefs: &ipn.Prefs{ - ControlURL: ipn.DefaultControlURL, - WantRunning: false, - Hostname: "foo", - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: ipn.DefaultControlURL, + WantRunning: false, + Hostname: "foo", + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --accept-dns --hostname=foo", }, @@ -198,10 +201,11 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "hostname_changing_explicitly", flags: []string{"--hostname=bar"}, curPrefs: &ipn.Prefs{ - ControlURL: ipn.DefaultControlURL, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, - Hostname: "foo", + ControlURL: ipn.DefaultControlURL, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + Hostname: "foo", + NoStatefulFiltering: opt.NewBool(true), }, want: "", }, @@ -209,10 +213,11 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "hostname_changing_empty_explicitly", flags: []string{"--hostname="}, curPrefs: &ipn.Prefs{ - ControlURL: ipn.DefaultControlURL, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, - Hostname: "foo", + ControlURL: ipn.DefaultControlURL, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + Hostname: "foo", + NoStatefulFiltering: opt.NewBool(true), }, want: "", }, @@ -228,10 +233,11 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "implicit_operator_change", flags: []string{"--hostname=foo"}, curPrefs: &ipn.Prefs{ - ControlURL: ipn.DefaultControlURL, - OperatorUser: "alice", - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: ipn.DefaultControlURL, + OperatorUser: "alice", + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, curUser: "eve", want: accidentalUpPrefix + " --hostname=foo --operator=alice", @@ -240,10 +246,11 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "implicit_operator_matches_shell_user", flags: []string{"--hostname=foo"}, curPrefs: &ipn.Prefs{ - ControlURL: ipn.DefaultControlURL, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, - OperatorUser: "alice", + ControlURL: ipn.DefaultControlURL, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + OperatorUser: "alice", + NoStatefulFiltering: opt.NewBool(true), }, curUser: "alice", want: "", @@ -260,6 +267,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"), }, + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --advertise-routes=10.0.42.0/24 --advertise-exit-node", }, @@ -275,6 +283,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"), }, + NoStatefulFiltering: opt.NewBool(true), }, want: "", }, @@ -290,6 +299,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"), }, + NoStatefulFiltering: opt.NewBool(true), }, want: "", }, @@ -297,9 +307,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "advertise_exit_node", // Issue 1859 flags: []string{"--advertise-exit-node"}, curPrefs: &ipn.Prefs{ - ControlURL: ipn.DefaultControlURL, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: ipn.DefaultControlURL, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, want: "", }, @@ -314,6 +325,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { AdvertiseRoutes: []netip.Prefix{ netip.MustParsePrefix("1.2.0.0/16"), }, + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --advertise-exit-node --advertise-routes=1.2.0.0/16", }, @@ -329,6 +341,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { netip.MustParsePrefix("::/0"), netip.MustParsePrefix("1.2.0.0/16"), }, + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --advertise-exit-node --advertise-routes=1.2.0.0/16", }, @@ -340,7 +353,8 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { CorpDNS: true, NetfilterMode: preftype.NetfilterOn, - ExitNodeID: "fooID", + ExitNodeID: "fooID", + NoStatefulFiltering: opt.NewBool(true), }, want: "", }, @@ -362,8 +376,9 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"), }, - NetfilterMode: preftype.NetfilterNoDivert, - OperatorUser: "alice", + NetfilterMode: preftype.NetfilterNoDivert, + OperatorUser: "alice", + NoStatefulFiltering: opt.NewBool(true), }, curUser: "eve", want: accidentalUpPrefix + " --force-reauth --accept-dns=false --accept-routes --advertise-exit-node --advertise-routes=10.0.0.0/16 --advertise-tags=tag:foo,tag:bar --exit-node=100.64.5.6 --hostname=myhostname --netfilter-mode=nodivert --operator=alice --shields-up", @@ -384,8 +399,9 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { AdvertiseRoutes: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/16"), }, - NetfilterMode: preftype.NetfilterNoDivert, - OperatorUser: "alice", + NetfilterMode: preftype.NetfilterNoDivert, + OperatorUser: "alice", + NoStatefulFiltering: opt.NewBool(true), }, curUser: "eve", want: accidentalUpPrefix + " --hostname=newhostname --accept-dns=false --accept-routes --advertise-routes=10.0.0.0/16 --advertise-tags=tag:foo,tag:bar --exit-node=100.64.5.6 --netfilter-mode=nodivert --operator=alice --shields-up", @@ -394,10 +410,11 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "loggedout_is_implicit", flags: []string{"--hostname=foo"}, curPrefs: &ipn.Prefs{ - ControlURL: ipn.DefaultControlURL, - LoggedOut: true, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: ipn.DefaultControlURL, + LoggedOut: true, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, want: "", // not an error. LoggedOut is implicit. }, @@ -440,6 +457,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { netip.MustParsePrefix("::/0"), netip.MustParsePrefix("1.2.0.0/16"), }, + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --operator=expbits --advertise-exit-node --advertise-routes=1.2.0.0/16", }, @@ -455,6 +473,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { netip.MustParsePrefix("::/0"), netip.MustParsePrefix("1.2.0.0/16"), }, + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --advertise-routes=1.2.0.0/16 --operator=expbits --advertise-exit-node", }, @@ -467,7 +486,8 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { CorpDNS: true, NetfilterMode: preftype.NetfilterOn, - Hostname: "foo", + Hostname: "foo", + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --auth-key=secretrand --force-reauth=false --reset --hostname=foo", }, @@ -479,7 +499,8 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { CorpDNS: true, NetfilterMode: preftype.NetfilterOn, - ExitNodeIP: netip.MustParseAddr("100.64.5.4"), + ExitNodeIP: netip.MustParseAddr("100.64.5.4"), + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --hostname=foo --exit-node=100.64.5.4", }, @@ -492,7 +513,8 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { CorpDNS: true, NetfilterMode: preftype.NetfilterOn, - ExitNodeID: "some_stable_id", + ExitNodeID: "some_stable_id", + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --hostname=foo --exit-node=100.64.5.7", }, @@ -507,6 +529,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { ExitNodeAllowLANAccess: true, ExitNodeID: "some_stable_id", + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --hostname=foo --exit-node-allow-lan-access --exit-node=100.2.3.4", }, @@ -514,9 +537,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "ignore_login_server_synonym", flags: []string{"--login-server=https://controlplane.tailscale.com"}, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, want: "", // not an error }, @@ -524,9 +548,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "ignore_login_server_synonym_on_other_change", flags: []string{"--netfilter-mode=off"}, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - CorpDNS: false, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + CorpDNS: false, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, want: accidentalUpPrefix + " --netfilter-mode=off --accept-dns=false", }, @@ -536,10 +561,11 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "synology_permit_omit_accept_routes", flags: []string{"--hostname=foo"}, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - CorpDNS: true, - RouteAll: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + CorpDNS: true, + RouteAll: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, goos: "linux", distro: distro.Synology, @@ -551,10 +577,11 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "not_synology_dont_permit_omit_accept_routes", flags: []string{"--hostname=foo"}, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - CorpDNS: true, - RouteAll: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + CorpDNS: true, + RouteAll: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, goos: "linux", distro: "", // not Synology @@ -564,10 +591,11 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { name: "profile_name_ignored_in_up", flags: []string{"--hostname=foo"}, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, - ProfileName: "foo", + ControlURL: "https://login.tailscale.com", + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + ProfileName: "foo", + NoStatefulFiltering: opt.NewBool(true), }, goos: "linux", want: "", @@ -630,7 +658,7 @@ func TestPrefsFromUpArgs(t *testing.T) { ControlURL: ipn.DefaultControlURL, WantRunning: true, NoSNAT: false, - NoStatefulFiltering: "false", + NoStatefulFiltering: "true", NetfilterMode: preftype.NetfilterOn, CorpDNS: true, AutoUpdate: ipn.AutoUpdatePrefs{ @@ -648,7 +676,7 @@ func TestPrefsFromUpArgs(t *testing.T) { CorpDNS: true, RouteAll: true, NoSNAT: false, - NoStatefulFiltering: "false", + NoStatefulFiltering: "true", NetfilterMode: preftype.NetfilterOn, AutoUpdate: ipn.AutoUpdatePrefs{ Check: true, @@ -666,7 +694,7 @@ func TestPrefsFromUpArgs(t *testing.T) { netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"), }, - NoStatefulFiltering: "false", + NoStatefulFiltering: "true", NetfilterMode: preftype.NetfilterOn, AutoUpdate: ipn.AutoUpdatePrefs{ Check: true, @@ -1033,10 +1061,11 @@ func TestUpdatePrefs(t *testing.T) { name: "change_login_server", flags: []string{"--login-server=https://localhost:1000"}, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, env: upCheckEnv{backendState: "Running"}, wantSimpleUp: true, @@ -1047,10 +1076,11 @@ func TestUpdatePrefs(t *testing.T) { name: "change_tags", flags: []string{"--advertise-tags=tag:foo"}, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, env: upCheckEnv{backendState: "Running"}, }, @@ -1059,10 +1089,11 @@ func TestUpdatePrefs(t *testing.T) { name: "explicit_empty_operator", flags: []string{"--operator="}, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, - OperatorUser: "somebody", + ControlURL: "https://login.tailscale.com", + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + OperatorUser: "somebody", + NoStatefulFiltering: opt.NewBool(true), }, env: upCheckEnv{user: "somebody", backendState: "Running"}, wantJustEditMP: &ipn.MaskedPrefs{ @@ -1079,10 +1110,11 @@ func TestUpdatePrefs(t *testing.T) { name: "enable_ssh", flags: []string{"--ssh"}, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, wantJustEditMP: &ipn.MaskedPrefs{ RunSSHSet: true, @@ -1099,11 +1131,12 @@ func TestUpdatePrefs(t *testing.T) { name: "disable_ssh", flags: []string{"--ssh=false"}, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, - CorpDNS: true, - RunSSH: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, + CorpDNS: true, + RunSSH: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, wantJustEditMP: &ipn.MaskedPrefs{ RunSSHSet: true, @@ -1123,11 +1156,12 @@ func TestUpdatePrefs(t *testing.T) { flags: []string{"--ssh=false"}, sshOverTailscale: true, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, - RunSSH: true, + ControlURL: "https://login.tailscale.com", + Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + RunSSH: true, + NoStatefulFiltering: opt.NewBool(true), }, wantJustEditMP: &ipn.MaskedPrefs{ RunSSHSet: true, @@ -1146,10 +1180,11 @@ func TestUpdatePrefs(t *testing.T) { flags: []string{"--ssh=true"}, sshOverTailscale: true, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, wantJustEditMP: &ipn.MaskedPrefs{ RunSSHSet: true, @@ -1168,10 +1203,11 @@ func TestUpdatePrefs(t *testing.T) { flags: []string{"--ssh=true", "--accept-risk=lose-ssh"}, sshOverTailscale: true, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, wantJustEditMP: &ipn.MaskedPrefs{ RunSSHSet: true, @@ -1189,11 +1225,12 @@ func TestUpdatePrefs(t *testing.T) { flags: []string{"--ssh=false", "--accept-risk=lose-ssh"}, sshOverTailscale: true, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, - CorpDNS: true, - RunSSH: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, + CorpDNS: true, + RunSSH: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, wantJustEditMP: &ipn.MaskedPrefs{ RunSSHSet: true, @@ -1211,9 +1248,10 @@ func TestUpdatePrefs(t *testing.T) { flags: []string{"--force-reauth"}, sshOverTailscale: true, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, env: upCheckEnv{backendState: "Running"}, wantErrSubtr: "aborted, no changes made", @@ -1223,9 +1261,10 @@ func TestUpdatePrefs(t *testing.T) { flags: []string{"--force-reauth", "--accept-risk=lose-ssh"}, sshOverTailscale: true, curPrefs: &ipn.Prefs{ - ControlURL: "https://login.tailscale.com", - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: "https://login.tailscale.com", + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, wantJustEditMP: nil, env: upCheckEnv{backendState: "Running"}, @@ -1234,9 +1273,10 @@ func TestUpdatePrefs(t *testing.T) { name: "advertise_connector", flags: []string{"--advertise-connector"}, curPrefs: &ipn.Prefs{ - ControlURL: ipn.DefaultControlURL, - CorpDNS: true, - NetfilterMode: preftype.NetfilterOn, + ControlURL: ipn.DefaultControlURL, + CorpDNS: true, + NetfilterMode: preftype.NetfilterOn, + NoStatefulFiltering: opt.NewBool(true), }, wantJustEditMP: &ipn.MaskedPrefs{ AppConnectorSet: true, @@ -1259,6 +1299,7 @@ func TestUpdatePrefs(t *testing.T) { AppConnector: ipn.AppConnectorPrefs{ Advertise: true, }, + NoStatefulFiltering: opt.NewBool(true), }, wantJustEditMP: &ipn.MaskedPrefs{ AppConnectorSet: true, diff --git a/cmd/tailscale/cli/set.go b/cmd/tailscale/cli/set.go index 126105669..0e9d35924 100644 --- a/cmd/tailscale/cli/set.go +++ b/cmd/tailscale/cli/set.go @@ -103,7 +103,7 @@ func newSetFlagSet(goos string, setArgs *setArgsT) *flag.FlagSet { 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.BoolVar(&setArgs.statefulFiltering, "stateful-filtering", false, "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)") diff --git a/cmd/tailscale/cli/up.go b/cmd/tailscale/cli/up.go index 602e1f1ab..ffcc64497 100644 --- a/cmd/tailscale/cli/up.go +++ b/cmd/tailscale/cli/up.go @@ -121,7 +121,7 @@ func newUpFlagSet(goos string, upArgs *upArgsT, cmd string) *flag.FlagSet { switch goos { case "linux": upf.BoolVar(&upArgs.snat, "snat-subnet-routes", true, "source NAT traffic to local routes advertised with --advertise-routes") - upf.BoolVar(&upArgs.statefulFiltering, "stateful-filtering", true, "apply stateful filtering to forwarded packets (subnet routers, exit nodes, etc.)") + upf.BoolVar(&upArgs.statefulFiltering, "stateful-filtering", false, "apply stateful filtering to forwarded packets (subnet routers, exit nodes, etc.)") upf.StringVar(&upArgs.netfilterMode, "netfilter-mode", defaultNetfilterMode(), "netfilter mode (one of on, nodivert, off)") case "windows": upf.BoolVar(&upArgs.forceDaemon, "unattended", false, "run in \"Unattended Mode\" where Tailscale keeps running even after the current GUI user logs out (Windows-only)") diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 7e5384592..5ec03c505 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -4186,18 +4186,7 @@ func (b *LocalBackend) routerConfig(cfg *wgcfg.Config, prefs ipn.PrefsView, oneC } var doStatefulFiltering bool - if v, ok := prefs.NoStatefulFiltering().Get(); !ok { - // The stateful filtering preference isn't explicitly set; this is - // unexpected since we expect it to be set during the profile - // backfill, but to be safe let's enable stateful filtering - // absent further information. - doStatefulFiltering = true - b.logf("[unexpected] NoStatefulFiltering preference not set; enabling stateful filtering") - } else if v { - // The preferences explicitly say "no stateful filtering", so - // we don't do it. - doStatefulFiltering = false - } else { + if v, ok := prefs.NoStatefulFiltering().Get(); ok && !v { // The preferences explicitly "do stateful filtering" is turned // off, or to expand the double negative, to do stateful // filtering. Do so. diff --git a/ipn/ipnlocal/profiles.go b/ipn/ipnlocal/profiles.go index d50a141ad..b01ff836d 100644 --- a/ipn/ipnlocal/profiles.go +++ b/ipn/ipnlocal/profiles.go @@ -354,10 +354,6 @@ func (pm *profileManager) loadSavedPrefs(key ipn.StateKey) (ipn.PrefsView, error return ipn.PrefsView{}, err } savedPrefs := ipn.NewPrefs() - // NewPrefs sets a default NoStatefulFiltering, but we want to actually see - // if the saved state had an empty value. The empty value gets migrated - // based on NoSNAT, while a default "false" does not. - savedPrefs.NoStatefulFiltering = "" if err := ipn.PrefsFromBytes(bs, savedPrefs); err != nil { return ipn.PrefsView{}, fmt.Errorf("parsing saved prefs: %v", err) } @@ -382,32 +378,6 @@ func (pm *profileManager) loadSavedPrefs(key ipn.StateKey) (ipn.PrefsView, error savedPrefs.AutoUpdate.Apply.Clear() } - // Backfill a missing NoStatefulFiltering field based on the value of - // the NoSNAT field; we want to apply stateful filtering in all cases - // *except* where the user has disabled SNAT. - // - // Only backfill if the user hasn't set a value for - // NoStatefulFiltering, however. - _, haveNoStateful := savedPrefs.NoStatefulFiltering.Get() - if !haveNoStateful { - if savedPrefs.NoSNAT { - pm.logf("backfilling NoStatefulFiltering field to true because NoSNAT is set") - - // No SNAT: no stateful filtering - savedPrefs.NoStatefulFiltering.Set(true) - } else { - pm.logf("backfilling NoStatefulFiltering field to false because NoSNAT is not set") - - // SNAT (default): apply stateful filtering - savedPrefs.NoStatefulFiltering.Set(false) - } - - // Write back to the preferences store now that we've updated it. - if err := pm.writePrefsToStore(key, savedPrefs.View()); err != nil { - return ipn.PrefsView{}, err - } - } - return savedPrefs.View(), nil } diff --git a/ipn/ipnlocal/profiles_test.go b/ipn/ipnlocal/profiles_test.go index 113aaec65..01d49c230 100644 --- a/ipn/ipnlocal/profiles_test.go +++ b/ipn/ipnlocal/profiles_test.go @@ -4,7 +4,6 @@ package ipnlocal import ( - "encoding/json" "fmt" "os/user" "strconv" @@ -13,14 +12,12 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "tailscale.com/clientupdate" - "tailscale.com/envknob" "tailscale.com/health" "tailscale.com/ipn" "tailscale.com/ipn/store/mem" "tailscale.com/tailcfg" "tailscale.com/types/key" "tailscale.com/types/logger" - "tailscale.com/types/opt" "tailscale.com/types/persist" "tailscale.com/util/must" ) @@ -604,89 +601,6 @@ func TestProfileManagementWindows(t *testing.T) { } } -func TestProfileBackfillStatefulFiltering(t *testing.T) { - envknob.Setenv("TS_DEBUG_PROFILES", "true") - - tests := []struct { - noSNAT bool - noStateful opt.Bool - want bool - }{ - // Default: NoSNAT is false, NoStatefulFiltering is false, so - // we want it to stay false. - {false, "false", false}, - - // NoSNAT being set to true and NoStatefulFiltering being false - // should result in NoStatefulFiltering still being false, - // since it was explicitly set. - {true, "false", false}, - - // If NoSNAT is false, and NoStatefulFiltering is unset, we - // backfill it to 'false'. - {false, "", false}, - - // If NoSNAT is true, and NoStatefulFiltering is unset, we - // backfill to 'true' to not break users of NoSNAT. - // - // In other words: if the user is not using SNAT, they almost - // certainly also don't want to use stateful filtering. - {true, "", true}, - - // However, if the user specifies both NoSNAT and stateful - // filtering, don't change that. - {true, "true", true}, - {false, "true", true}, - } - - for _, tt := range tests { - t.Run(fmt.Sprintf("noSNAT=%v,noStateful=%q", tt.noSNAT, tt.noStateful), func(t *testing.T) { - prefs := ipn.NewPrefs() - prefs.Persist = &persist.Persist{ - NodeID: tailcfg.StableNodeID("node1"), - UserProfile: tailcfg.UserProfile{ - ID: tailcfg.UserID(1), - LoginName: "user1@example.com", - }, - } - - prefs.NoSNAT = tt.noSNAT - prefs.NoStatefulFiltering = tt.noStateful - - // Make enough of a state store to load the prefs. - const profileName = "profile1" - bn := must.Get(json.Marshal(map[string]any{ - string(ipn.CurrentProfileStateKey): []byte(profileName), - string(ipn.KnownProfilesStateKey): must.Get(json.Marshal(map[ipn.ProfileID]*ipn.LoginProfile{ - profileName: { - ID: "profile1-id", - Key: profileName, - }, - })), - profileName: prefs.ToBytes(), - })) - - store := new(mem.Store) - err := store.LoadFromJSON([]byte(bn)) - if err != nil { - t.Fatal(err) - } - - ht := new(health.Tracker) - pm, err := newProfileManagerWithGOOS(store, t.Logf, ht, "linux") - if err != nil { - t.Fatal(err) - } - - // Get the current profile and verify that we backfilled our - // StatefulFiltering boolean. - pf := pm.CurrentPrefs() - if !pf.NoStatefulFiltering().EqualBool(tt.want) { - t.Fatalf("got NoStatefulFiltering=%q, want %v", pf.NoStatefulFiltering(), tt.want) - } - }) - } -} - // TestDefaultPrefs tests that defaultPrefs is just NewPrefs with // LoggedOut=true (the Prefs we use before connecting to control). We shouldn't // be putting any defaulting there, and instead put all defaults in NewPrefs. diff --git a/ipn/prefs.go b/ipn/prefs.go index 179b66e4b..f3c9758ac 100644 --- a/ipn/prefs.go +++ b/ipn/prefs.go @@ -191,17 +191,16 @@ type Prefs struct { // Linux-only. NoSNAT bool - // NoStatefulFiltering specifies whether to apply stateful filtering - // when advertising routes in AdvertiseRoutes. The default is to apply + // NoStatefulFiltering specifies whether to apply stateful filtering when + // advertising routes in AdvertiseRoutes. The default is to not apply // stateful filtering. // // To allow inbound connections from advertised routes, both NoSNAT and // NoStatefulFiltering must be true. // - // This is an opt.Bool because it was added after NoSNAT, but is backfilled - // based on the value of that parameter. We need to treat it as a tristate: - // true, false, or unset, and backfill based on that value. See - // ipn/ipnlocal for more details on the backfill. + // This is an opt.Bool because it was first added after NoSNAT, with a + // backfill based on the value of that parameter. The backfill has been + // removed since then, but the field remains an opt.Bool. // // Linux-only. NoStatefulFiltering opt.Bool `json:",omitempty"` @@ -666,7 +665,7 @@ func NewPrefs() *Prefs { CorpDNS: true, WantRunning: false, NetfilterMode: preftype.NetfilterOn, - NoStatefulFiltering: opt.NewBool(false), + NoStatefulFiltering: opt.NewBool(true), AutoUpdate: AutoUpdatePrefs{ Check: true, Apply: opt.Bool("unset"), diff --git a/wgengine/router/router_linux.go b/wgengine/router/router_linux.go index c7763229d..41c5b128c 100644 --- a/wgengine/router/router_linux.go +++ b/wgengine/router/router_linux.go @@ -469,7 +469,7 @@ func (r *linuxRouter) updateStatefulFilteringWithDockerWarning(cfg *Config) { if _, found := ifstate.Interface["docker0"]; found { r.health.SetWarnable(warnStatefulFilteringWithDocker, fmt.Errorf(""+ "Stateful filtering is enabled and Docker was detected; this may prevent Docker containers "+ - "on this host from connecting to Tailscale nodes. "+ + "on this host from resolving DNS and connecting to Tailscale nodes. "+ "See https://tailscale.com/s/stateful-docker", )) return