ipn,wgengine: remove vestigial Prefs.AllowSingleHosts

It was requested by the first customer 4-5 years ago and only used
for a brief moment of time. We later added netmap visibility trimming
which removes the need for this.

It's been hidden by the CLI for quite some time and never documented
anywhere else.

This keeps the CLI flag, though, out of caution. It just returns an
error if it's set to anything but true (its default).

Fixes #12058

Change-Id: I7514ba572e7b82519b04ed603ff9f3bdbaecfda7
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/12181/head
Brad Fitzpatrick 1 month ago committed by Brad Fitzpatrick
parent 1384c24e41
commit 964282d34f

@ -191,7 +191,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
Hostname: "foo", Hostname: "foo",
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AllowSingleHosts: true,
}, },
want: accidentalUpPrefix + " --accept-dns --hostname=foo", want: accidentalUpPrefix + " --accept-dns --hostname=foo",
}, },
@ -202,7 +201,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AllowSingleHosts: true,
Hostname: "foo", Hostname: "foo",
}, },
want: "", want: "",
@ -214,7 +212,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AllowSingleHosts: true,
Hostname: "foo", Hostname: "foo",
}, },
want: "", want: "",
@ -233,7 +230,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
OperatorUser: "alice", OperatorUser: "alice",
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -245,7 +241,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--hostname=foo"}, flags: []string{"--hostname=foo"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
OperatorUser: "alice", OperatorUser: "alice",
@ -258,7 +253,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--advertise-routes=10.0.42.0/24"}, flags: []string{"--advertise-routes=10.0.42.0/24"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{ AdvertiseRoutes: []netip.Prefix{
@ -274,7 +268,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--advertise-routes=10.0.42.0/24", "--advertise-exit-node=false"}, flags: []string{"--advertise-routes=10.0.42.0/24", "--advertise-exit-node=false"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{ AdvertiseRoutes: []netip.Prefix{
@ -290,7 +283,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--advertise-routes=11.1.43.0/24,0.0.0.0/0,::/0"}, flags: []string{"--advertise-routes=11.1.43.0/24,0.0.0.0/0,::/0"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{ AdvertiseRoutes: []netip.Prefix{
@ -306,7 +298,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--advertise-exit-node"}, flags: []string{"--advertise-exit-node"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -317,7 +308,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--advertise-exit-node"}, flags: []string{"--advertise-exit-node"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
@ -332,7 +322,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--advertise-exit-node"}, flags: []string{"--advertise-exit-node"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{ AdvertiseRoutes: []netip.Prefix{
@ -348,7 +337,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--exit-node="}, flags: []string{"--exit-node="},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
@ -363,7 +351,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
WantRunning: true, WantRunning: true,
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
RouteAll: true, RouteAll: true,
AllowSingleHosts: false,
ExitNodeIP: netip.MustParseAddr("100.64.5.6"), ExitNodeIP: netip.MustParseAddr("100.64.5.6"),
CorpDNS: false, CorpDNS: false,
ShieldsUp: true, ShieldsUp: true,
@ -379,7 +366,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
OperatorUser: "alice", OperatorUser: "alice",
}, },
curUser: "eve", 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 --host-routes=false --hostname=myhostname --netfilter-mode=nodivert --operator=alice --shields-up", 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",
}, },
{ {
name: "remove_all_implicit_except_hostname", name: "remove_all_implicit_except_hostname",
@ -388,7 +375,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
WantRunning: true, WantRunning: true,
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
RouteAll: true, RouteAll: true,
AllowSingleHosts: false,
ExitNodeIP: netip.MustParseAddr("100.64.5.6"), ExitNodeIP: netip.MustParseAddr("100.64.5.6"),
CorpDNS: false, CorpDNS: false,
ShieldsUp: true, ShieldsUp: true,
@ -402,7 +388,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
OperatorUser: "alice", OperatorUser: "alice",
}, },
curUser: "eve", 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 --host-routes=false --netfilter-mode=nodivert --operator=alice --shields-up", 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",
}, },
{ {
name: "loggedout_is_implicit", name: "loggedout_is_implicit",
@ -410,7 +396,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
LoggedOut: true, LoggedOut: true,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -423,7 +408,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--advertise-exit-node"}, flags: []string{"--advertise-exit-node"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
RouteAll: true, RouteAll: true,
@ -438,7 +422,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--accept-dns"}, flags: []string{"--accept-dns"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
NetfilterMode: preftype.NetfilterNoDivert, // we never had this bug, but pretend it got set non-zero on Windows somehow NetfilterMode: preftype.NetfilterNoDivert, // we never had this bug, but pretend it got set non-zero on Windows somehow
}, },
@ -450,7 +433,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--operator=expbits"}, flags: []string{"--operator=expbits"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{ AdvertiseRoutes: []netip.Prefix{
@ -466,7 +448,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--operator=expbits", "--advertise-routes=1.2.0.0/16"}, flags: []string{"--operator=expbits", "--advertise-routes=1.2.0.0/16"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{ AdvertiseRoutes: []netip.Prefix{
@ -485,7 +466,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
WantRunning: false, WantRunning: false,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AllowSingleHosts: true,
Hostname: "foo", Hostname: "foo",
}, },
@ -496,7 +476,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--hostname=foo"}, flags: []string{"--hostname=foo"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
@ -510,7 +489,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
curExitNodeIP: netip.MustParseAddr("100.64.5.7"), curExitNodeIP: netip.MustParseAddr("100.64.5.7"),
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
@ -524,7 +502,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
curExitNodeIP: netip.MustParseAddr("100.2.3.4"), curExitNodeIP: netip.MustParseAddr("100.2.3.4"),
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
@ -538,7 +515,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--login-server=https://controlplane.tailscale.com"}, flags: []string{"--login-server=https://controlplane.tailscale.com"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -549,7 +525,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags: []string{"--netfilter-mode=off"}, flags: []string{"--netfilter-mode=off"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
AllowSingleHosts: true,
CorpDNS: false, CorpDNS: false,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -563,7 +538,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
CorpDNS: true, CorpDNS: true,
AllowSingleHosts: true,
RouteAll: true, RouteAll: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -579,7 +553,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
CorpDNS: true, CorpDNS: true,
AllowSingleHosts: true,
RouteAll: true, RouteAll: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -593,7 +566,6 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
CorpDNS: true, CorpDNS: true,
AllowSingleHosts: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
ProfileName: "foo", ProfileName: "foo",
}, },
@ -661,7 +633,6 @@ func TestPrefsFromUpArgs(t *testing.T) {
NoStatefulFiltering: "false", NoStatefulFiltering: "false",
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
CorpDNS: true, CorpDNS: true,
AllowSingleHosts: true,
AutoUpdate: ipn.AutoUpdatePrefs{ AutoUpdate: ipn.AutoUpdatePrefs{
Check: true, Check: true,
}, },
@ -675,7 +646,6 @@ func TestPrefsFromUpArgs(t *testing.T) {
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
WantRunning: true, WantRunning: true,
CorpDNS: true, CorpDNS: true,
AllowSingleHosts: true,
RouteAll: true, RouteAll: true,
NoSNAT: false, NoSNAT: false,
NoStatefulFiltering: "false", NoStatefulFiltering: "false",
@ -691,7 +661,6 @@ func TestPrefsFromUpArgs(t *testing.T) {
want: &ipn.Prefs{ want: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
WantRunning: true, WantRunning: true,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
AdvertiseRoutes: []netip.Prefix{ AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("0.0.0.0/0"),
@ -922,6 +891,9 @@ func TestPrefFlagMapping(t *testing.T) {
continue continue
} }
switch prefName { switch prefName {
case "AllowSingleHosts":
// Fake pref for downgrade compat. See #12058.
continue
case "WantRunning", "Persist", "LoggedOut": case "WantRunning", "Persist", "LoggedOut":
// All explicitly handled (ignored) by checkForAccidentalSettingReverts. // All explicitly handled (ignored) by checkForAccidentalSettingReverts.
continue continue
@ -1029,7 +1001,6 @@ func TestUpdatePrefs(t *testing.T) {
wantJustEditMP: &ipn.MaskedPrefs{ wantJustEditMP: &ipn.MaskedPrefs{
AdvertiseRoutesSet: true, AdvertiseRoutesSet: true,
AdvertiseTagsSet: true, AdvertiseTagsSet: true,
AllowSingleHostsSet: true,
AppConnectorSet: true, AppConnectorSet: true,
ControlURLSet: true, ControlURLSet: true,
CorpDNSSet: true, CorpDNSSet: true,
@ -1064,7 +1035,6 @@ func TestUpdatePrefs(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}},
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -1079,7 +1049,6 @@ func TestUpdatePrefs(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}},
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -1092,7 +1061,6 @@ func TestUpdatePrefs(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
CorpDNS: true, CorpDNS: true,
AllowSingleHosts: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
OperatorUser: "somebody", OperatorUser: "somebody",
}, },
@ -1113,7 +1081,6 @@ func TestUpdatePrefs(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}},
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -1134,7 +1101,6 @@ func TestUpdatePrefs(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}},
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
RunSSH: true, RunSSH: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
@ -1159,7 +1125,6 @@ func TestUpdatePrefs(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}},
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
RunSSH: true, RunSSH: true,
@ -1183,7 +1148,6 @@ func TestUpdatePrefs(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}},
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -1206,7 +1170,6 @@ func TestUpdatePrefs(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}},
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -1228,7 +1191,6 @@ func TestUpdatePrefs(t *testing.T) {
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}}, Persist: &persist.Persist{UserProfile: tailcfg.UserProfile{LoginName: "crawshaw.github"}},
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
RunSSH: true, RunSSH: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
@ -1250,7 +1212,6 @@ func TestUpdatePrefs(t *testing.T) {
sshOverTailscale: true, sshOverTailscale: true,
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -1263,7 +1224,6 @@ func TestUpdatePrefs(t *testing.T) {
sshOverTailscale: true, sshOverTailscale: true,
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com", ControlURL: "https://login.tailscale.com",
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -1275,7 +1235,6 @@ func TestUpdatePrefs(t *testing.T) {
flags: []string{"--advertise-connector"}, flags: []string{"--advertise-connector"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
}, },
@ -1295,7 +1254,6 @@ func TestUpdatePrefs(t *testing.T) {
flags: []string{"--advertise-connector=false"}, flags: []string{"--advertise-connector=false"},
curPrefs: &ipn.Prefs{ curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL, ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,
AppConnector: ipn.AppConnectorPrefs{ AppConnector: ipn.AppConnectorPrefs{

@ -104,7 +104,7 @@ func newUpFlagSet(goos string, upArgs *upArgsT, cmd string) *flag.FlagSet {
upf.StringVar(&upArgs.server, "login-server", ipn.DefaultControlURL, "base URL of control server") upf.StringVar(&upArgs.server, "login-server", ipn.DefaultControlURL, "base URL of control server")
upf.BoolVar(&upArgs.acceptRoutes, "accept-routes", acceptRouteDefault(goos), "accept routes advertised by other Tailscale nodes") upf.BoolVar(&upArgs.acceptRoutes, "accept-routes", acceptRouteDefault(goos), "accept routes advertised by other Tailscale nodes")
upf.BoolVar(&upArgs.acceptDNS, "accept-dns", true, "accept DNS configuration from the admin panel") upf.BoolVar(&upArgs.acceptDNS, "accept-dns", true, "accept DNS configuration from the admin panel")
upf.BoolVar(&upArgs.singleRoutes, "host-routes", true, hidden+"install host routes to other Tailscale nodes") upf.Var(notFalseVar{}, "host-routes", hidden+"install host routes to other Tailscale nodes (must be true as of Tailscale 1.67+)")
upf.StringVar(&upArgs.exitNodeIP, "exit-node", "", "Tailscale exit node (IP or base name) for internet traffic, or empty string to not use an exit node") upf.StringVar(&upArgs.exitNodeIP, "exit-node", "", "Tailscale exit node (IP or base name) for internet traffic, or empty string to not use an exit node")
upf.BoolVar(&upArgs.exitNodeAllowLANAccess, "exit-node-allow-lan-access", false, "Allow direct access to the local network when routing traffic via an exit node") upf.BoolVar(&upArgs.exitNodeAllowLANAccess, "exit-node-allow-lan-access", false, "Allow direct access to the local network when routing traffic via an exit node")
upf.BoolVar(&upArgs.shieldsUp, "shields-up", false, "don't allow incoming connections") upf.BoolVar(&upArgs.shieldsUp, "shields-up", false, "don't allow incoming connections")
@ -143,6 +143,18 @@ func newUpFlagSet(goos string, upArgs *upArgsT, cmd string) *flag.FlagSet {
return upf return upf
} }
// notFalseVar is is a flag.Value that can only be "true", if set.
type notFalseVar struct{}
func (notFalseVar) IsBoolFlag() bool { return true }
func (notFalseVar) Set(v string) error {
if v != "true" {
return fmt.Errorf("unsupported value; only 'true' is allowed")
}
return nil
}
func (notFalseVar) String() string { return "true" }
func defaultNetfilterMode() string { func defaultNetfilterMode() string {
if distro.Get() == distro.Synology { if distro.Get() == distro.Synology {
return "off" return "off"
@ -156,7 +168,6 @@ type upArgsT struct {
server string server string
acceptRoutes bool acceptRoutes bool
acceptDNS bool acceptDNS bool
singleRoutes bool
exitNodeIP string exitNodeIP string
exitNodeAllowLANAccess bool exitNodeAllowLANAccess bool
shieldsUp bool shieldsUp bool
@ -278,7 +289,6 @@ func prefsFromUpArgs(upArgs upArgsT, warnf logger.Logf, st *ipnstate.Status, goo
prefs.ExitNodeAllowLANAccess = upArgs.exitNodeAllowLANAccess prefs.ExitNodeAllowLANAccess = upArgs.exitNodeAllowLANAccess
prefs.CorpDNS = upArgs.acceptDNS prefs.CorpDNS = upArgs.acceptDNS
prefs.AllowSingleHosts = upArgs.singleRoutes
prefs.ShieldsUp = upArgs.shieldsUp prefs.ShieldsUp = upArgs.shieldsUp
prefs.RunSSH = upArgs.runSSH prefs.RunSSH = upArgs.runSSH
prefs.RunWebClient = upArgs.runWebClient prefs.RunWebClient = upArgs.runWebClient
@ -740,7 +750,6 @@ func init() {
addPrefFlagMapping("accept-dns", "CorpDNS") addPrefFlagMapping("accept-dns", "CorpDNS")
addPrefFlagMapping("accept-routes", "RouteAll") addPrefFlagMapping("accept-routes", "RouteAll")
addPrefFlagMapping("advertise-tags", "AdvertiseTags") addPrefFlagMapping("advertise-tags", "AdvertiseTags")
addPrefFlagMapping("host-routes", "AllowSingleHosts")
addPrefFlagMapping("hostname", "Hostname") addPrefFlagMapping("hostname", "Hostname")
addPrefFlagMapping("login-server", "ControlURL") addPrefFlagMapping("login-server", "ControlURL")
addPrefFlagMapping("netfilter-mode", "NetfilterMode") addPrefFlagMapping("netfilter-mode", "NetfilterMode")
@ -779,7 +788,7 @@ func addPrefFlagMapping(flagName string, prefNames ...string) {
// correspond to an ipn.Pref. // correspond to an ipn.Pref.
func preflessFlag(flagName string) bool { func preflessFlag(flagName string) bool {
switch flagName { switch flagName {
case "auth-key", "force-reauth", "reset", "qr", "json", "timeout", "accept-risk": case "auth-key", "force-reauth", "reset", "qr", "json", "timeout", "accept-risk", "host-routes":
return true return true
} }
return false return false
@ -975,8 +984,6 @@ func prefsToFlags(env upCheckEnv, prefs *ipn.Prefs) (flagVal map[string]any) {
set(prefs.ControlURL) set(prefs.ControlURL)
case "accept-routes": case "accept-routes":
set(prefs.RouteAll) set(prefs.RouteAll)
case "host-routes":
set(prefs.AllowSingleHosts)
case "accept-dns": case "accept-dns":
set(prefs.CorpDNS) set(prefs.CorpDNS)
case "shields-up": case "shields-up":

@ -300,7 +300,6 @@ func (i *jsIPN) run(jsCallbacks js.Value) {
UpdatePrefs: &ipn.Prefs{ UpdatePrefs: &ipn.Prefs{
ControlURL: i.controlURL, ControlURL: i.controlURL,
RouteAll: false, RouteAll: false,
AllowSingleHosts: true,
WantRunning: true, WantRunning: true,
Hostname: i.hostname, Hostname: i.hostname,
}, },

@ -40,7 +40,6 @@ func (src *Prefs) Clone() *Prefs {
var _PrefsCloneNeedsRegeneration = Prefs(struct { var _PrefsCloneNeedsRegeneration = Prefs(struct {
ControlURL string ControlURL string
RouteAll bool RouteAll bool
AllowSingleHosts bool
ExitNodeID tailcfg.StableNodeID ExitNodeID tailcfg.StableNodeID
ExitNodeIP netip.Addr ExitNodeIP netip.Addr
InternalExitNodePrior tailcfg.StableNodeID InternalExitNodePrior tailcfg.StableNodeID
@ -67,6 +66,7 @@ var _PrefsCloneNeedsRegeneration = Prefs(struct {
PostureChecking bool PostureChecking bool
NetfilterKind string NetfilterKind string
DriveShares []*drive.Share DriveShares []*drive.Share
AllowSingleHosts marshalAsTrueInJSON
Persist *persist.Persist Persist *persist.Persist
}{}) }{})

@ -67,7 +67,6 @@ func (v *PrefsView) UnmarshalJSON(b []byte) error {
func (v PrefsView) ControlURL() string { return v.ж.ControlURL } func (v PrefsView) ControlURL() string { return v.ж.ControlURL }
func (v PrefsView) RouteAll() bool { return v.ж.RouteAll } func (v PrefsView) RouteAll() bool { return v.ж.RouteAll }
func (v PrefsView) AllowSingleHosts() bool { return v.ж.AllowSingleHosts }
func (v PrefsView) ExitNodeID() tailcfg.StableNodeID { return v.ж.ExitNodeID } func (v PrefsView) ExitNodeID() tailcfg.StableNodeID { return v.ж.ExitNodeID }
func (v PrefsView) ExitNodeIP() netip.Addr { return v.ж.ExitNodeIP } func (v PrefsView) ExitNodeIP() netip.Addr { return v.ж.ExitNodeIP }
func (v PrefsView) InternalExitNodePrior() tailcfg.StableNodeID { return v.ж.InternalExitNodePrior } func (v PrefsView) InternalExitNodePrior() tailcfg.StableNodeID { return v.ж.InternalExitNodePrior }
@ -98,13 +97,13 @@ func (v PrefsView) NetfilterKind() string { return v.ж.Netfilte
func (v PrefsView) DriveShares() views.SliceView[*drive.Share, drive.ShareView] { func (v PrefsView) DriveShares() views.SliceView[*drive.Share, drive.ShareView] {
return views.SliceOfViews[*drive.Share, drive.ShareView](v.ж.DriveShares) return views.SliceOfViews[*drive.Share, drive.ShareView](v.ж.DriveShares)
} }
func (v PrefsView) AllowSingleHosts() marshalAsTrueInJSON { return v.ж.AllowSingleHosts }
func (v PrefsView) Persist() persist.PersistView { return v.ж.Persist.View() } func (v PrefsView) Persist() persist.PersistView { return v.ж.Persist.View() }
// A compilation failure here means this code must be regenerated, with the command at the top of this file. // A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _PrefsViewNeedsRegeneration = Prefs(struct { var _PrefsViewNeedsRegeneration = Prefs(struct {
ControlURL string ControlURL string
RouteAll bool RouteAll bool
AllowSingleHosts bool
ExitNodeID tailcfg.StableNodeID ExitNodeID tailcfg.StableNodeID
ExitNodeIP netip.Addr ExitNodeIP netip.Addr
InternalExitNodePrior tailcfg.StableNodeID InternalExitNodePrior tailcfg.StableNodeID
@ -131,6 +130,7 @@ var _PrefsViewNeedsRegeneration = Prefs(struct {
PostureChecking bool PostureChecking bool
NetfilterKind string NetfilterKind string
DriveShares []*drive.Share DriveShares []*drive.Share
AllowSingleHosts marshalAsTrueInJSON
Persist *persist.Persist Persist *persist.Persist
}{}) }{})

@ -3649,9 +3649,6 @@ func (b *LocalBackend) authReconfig() {
if prefs.RouteAll() { if prefs.RouteAll() {
flags |= netmap.AllowSubnetRoutes flags |= netmap.AllowSubnetRoutes
} }
if prefs.AllowSingleHosts() {
flags |= netmap.AllowSingleHosts
}
if hasPAC && disableSubnetsIfPAC { if hasPAC && disableSubnetsIfPAC {
if flags&netmap.AllowSubnetRoutes != 0 { if flags&netmap.AllowSubnetRoutes != 0 {
b.logf("authReconfig: have PAC; disabling subnet routes") b.logf("authReconfig: have PAC; disabling subnet routes")

@ -75,18 +75,6 @@ type Prefs struct {
// controlled by ExitNodeID/IP below. // controlled by ExitNodeID/IP below.
RouteAll bool RouteAll bool
// AllowSingleHosts specifies whether to install routes for each
// node IP on the tailscale network, in addition to a route for
// the whole network.
// This corresponds to the "tailscale up --host-routes" value,
// which defaults to true.
//
// TODO(danderson): why do we have this? It dumps a lot of stuff
// into the routing table, and a single network route _should_ be
// all that we need. But when I turn this off in my tailscaled,
// packets stop flowing. What's up with that?
AllowSingleHosts bool
// ExitNodeID and ExitNodeIP specify the node that should be used // ExitNodeID and ExitNodeIP specify the node that should be used
// as an exit node for internet traffic. At most one of these // as an exit node for internet traffic. At most one of these
// should be non-zero. // should be non-zero.
@ -252,6 +240,16 @@ type Prefs struct {
// by name. // by name.
DriveShares []*drive.Share DriveShares []*drive.Share
// AllowSingleHosts was a legacy field that was always true
// for the past 4.5 years. It controlled whether Tailscale
// peers got /32 or /127 routes for each other.
// As of 2024-05-17 we're starting to ignore it, but to let
// people still downgrade Tailscale versions and not break
// all peer-to-peer networking we still write it to disk (as JSON)
// so it can be loaded back by old versions.
// TODO(bradfitz): delete this in 2025 sometime. See #12058.
AllowSingleHosts marshalAsTrueInJSON
// The Persist field is named 'Config' in the file for backward // The Persist field is named 'Config' in the file for backward
// compatibility with earlier versions. // compatibility with earlier versions.
// TODO(apenwarr): We should move this out of here, it's not a pref. // TODO(apenwarr): We should move this out of here, it's not a pref.
@ -282,6 +280,13 @@ func (au1 AutoUpdatePrefs) Equals(au2 AutoUpdatePrefs) bool {
ok1 == ok2 ok1 == ok2
} }
type marshalAsTrueInJSON struct{}
var trueJSON = []byte("true")
func (marshalAsTrueInJSON) MarshalJSON() ([]byte, error) { return trueJSON, nil }
func (*marshalAsTrueInJSON) UnmarshalJSON([]byte) error { return nil }
// AppConnectorPrefs are the app connector settings for the node agent. // AppConnectorPrefs are the app connector settings for the node agent.
type AppConnectorPrefs struct { type AppConnectorPrefs struct {
// Advertise specifies whether the app connector subsystem is advertising // Advertise specifies whether the app connector subsystem is advertising
@ -299,7 +304,6 @@ type MaskedPrefs struct {
ControlURLSet bool `json:",omitempty"` ControlURLSet bool `json:",omitempty"`
RouteAllSet bool `json:",omitempty"` RouteAllSet bool `json:",omitempty"`
AllowSingleHostsSet bool `json:",omitempty"`
ExitNodeIDSet bool `json:",omitempty"` ExitNodeIDSet bool `json:",omitempty"`
ExitNodeIPSet bool `json:",omitempty"` ExitNodeIPSet bool `json:",omitempty"`
InternalExitNodePriorSet bool `json:",omitempty"` // Internal; can't be set by LocalAPI clients InternalExitNodePriorSet bool `json:",omitempty"` // Internal; can't be set by LocalAPI clients
@ -484,9 +488,6 @@ func (p *Prefs) pretty(goos string) string {
var sb strings.Builder var sb strings.Builder
sb.WriteString("Prefs{") sb.WriteString("Prefs{")
fmt.Fprintf(&sb, "ra=%v ", p.RouteAll) fmt.Fprintf(&sb, "ra=%v ", p.RouteAll)
if !p.AllowSingleHosts {
sb.WriteString("mesh=false ")
}
fmt.Fprintf(&sb, "dns=%v want=%v ", p.CorpDNS, p.WantRunning) fmt.Fprintf(&sb, "dns=%v want=%v ", p.CorpDNS, p.WantRunning)
if p.RunSSH { if p.RunSSH {
sb.WriteString("ssh=true ") sb.WriteString("ssh=true ")
@ -579,7 +580,6 @@ func (p *Prefs) Equals(p2 *Prefs) bool {
return p.ControlURL == p2.ControlURL && return p.ControlURL == p2.ControlURL &&
p.RouteAll == p2.RouteAll && p.RouteAll == p2.RouteAll &&
p.AllowSingleHosts == p2.AllowSingleHosts &&
p.ExitNodeID == p2.ExitNodeID && p.ExitNodeID == p2.ExitNodeID &&
p.ExitNodeIP == p2.ExitNodeIP && p.ExitNodeIP == p2.ExitNodeIP &&
p.InternalExitNodePrior == p2.InternalExitNodePrior && p.InternalExitNodePrior == p2.InternalExitNodePrior &&
@ -663,7 +663,6 @@ func NewPrefs() *Prefs {
ControlURL: "", ControlURL: "",
RouteAll: true, RouteAll: true,
AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
WantRunning: false, WantRunning: false,
NetfilterMode: preftype.NetfilterOn, NetfilterMode: preftype.NetfilterOn,

@ -38,7 +38,6 @@ func TestPrefsEqual(t *testing.T) {
prefsHandles := []string{ prefsHandles := []string{
"ControlURL", "ControlURL",
"RouteAll", "RouteAll",
"AllowSingleHosts",
"ExitNodeID", "ExitNodeID",
"ExitNodeIP", "ExitNodeIP",
"InternalExitNodePrior", "InternalExitNodePrior",
@ -65,6 +64,7 @@ func TestPrefsEqual(t *testing.T) {
"PostureChecking", "PostureChecking",
"NetfilterKind", "NetfilterKind",
"DriveShares", "DriveShares",
"AllowSingleHosts",
"Persist", "Persist",
} }
if have := fieldsOf(reflect.TypeFor[Prefs]()); !reflect.DeepEqual(have, prefsHandles) { if have := fieldsOf(reflect.TypeFor[Prefs]()); !reflect.DeepEqual(have, prefsHandles) {
@ -123,18 +123,6 @@ func TestPrefsEqual(t *testing.T) {
&Prefs{RouteAll: true}, &Prefs{RouteAll: true},
true, true,
}, },
{
&Prefs{AllowSingleHosts: true},
&Prefs{AllowSingleHosts: false},
false,
},
{
&Prefs{AllowSingleHosts: true},
&Prefs{AllowSingleHosts: true},
true,
},
{ {
&Prefs{ExitNodeID: "n1234"}, &Prefs{ExitNodeID: "n1234"},
&Prefs{}, &Prefs{},
@ -376,7 +364,7 @@ func checkPrefs(t *testing.T, p Prefs) {
p2b = new(Prefs) p2b = new(Prefs)
err = PrefsFromBytes(p2.ToBytes(), p2b) err = PrefsFromBytes(p2.ToBytes(), p2b)
if err != nil { if err != nil {
t.Fatalf("PrefsFromBytes(p2) failed\n") t.Fatalf("PrefsFromBytes(p2) failed: bytes=%q; err=%v\n", p2.ToBytes(), err)
} }
p2p := p2.Pretty() p2p := p2.Pretty()
p2bp := p2b.Pretty() p2bp := p2b.Pretty()
@ -427,34 +415,32 @@ func TestPrefsPretty(t *testing.T) {
{ {
Prefs{}, Prefs{},
"linux", "linux",
"Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off update=off Persist=nil}", "Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist=nil}",
}, },
{ {
Prefs{}, Prefs{},
"windows", "windows",
"Prefs{ra=false mesh=false dns=false want=false update=off Persist=nil}", "Prefs{ra=false dns=false want=false update=off Persist=nil}",
}, },
{ {
Prefs{ShieldsUp: true}, Prefs{ShieldsUp: true},
"windows", "windows",
"Prefs{ra=false mesh=false dns=false want=false shields=true update=off Persist=nil}", "Prefs{ra=false dns=false want=false shields=true update=off Persist=nil}",
}, },
{ {
Prefs{AllowSingleHosts: true}, Prefs{},
"windows", "windows",
"Prefs{ra=false dns=false want=false update=off Persist=nil}", "Prefs{ra=false dns=false want=false update=off Persist=nil}",
}, },
{ {
Prefs{ Prefs{
NotepadURLs: true, NotepadURLs: true,
AllowSingleHosts: true,
}, },
"windows", "windows",
"Prefs{ra=false dns=false want=false notepad=true update=off Persist=nil}", "Prefs{ra=false dns=false want=false notepad=true update=off Persist=nil}",
}, },
{ {
Prefs{ Prefs{
AllowSingleHosts: true,
WantRunning: true, WantRunning: true,
ForceDaemon: true, // server mode ForceDaemon: true, // server mode
}, },
@ -463,7 +449,6 @@ func TestPrefsPretty(t *testing.T) {
}, },
{ {
Prefs{ Prefs{
AllowSingleHosts: true,
WantRunning: true, WantRunning: true,
ControlURL: "http://localhost:1234", ControlURL: "http://localhost:1234",
AdvertiseTags: []string{"tag:foo", "tag:bar"}, AdvertiseTags: []string{"tag:foo", "tag:bar"},
@ -476,7 +461,7 @@ func TestPrefsPretty(t *testing.T) {
Persist: &persist.Persist{}, Persist: &persist.Persist{},
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off update=off Persist{lm=, o=, n= u=""}}`, `Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist{lm=, o=, n= u=""}}`,
}, },
{ {
Prefs{ Prefs{
@ -485,21 +470,21 @@ func TestPrefsPretty(t *testing.T) {
}, },
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off update=off Persist{lm=, o=, n=[B1VKl] u=""}}`, `Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist{lm=, o=, n=[B1VKl] u=""}}`,
}, },
{ {
Prefs{ Prefs{
ExitNodeIP: netip.MustParseAddr("1.2.3.4"), ExitNodeIP: netip.MustParseAddr("1.2.3.4"),
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false exit=1.2.3.4 lan=false routes=[] nf=off update=off Persist=nil}`, `Prefs{ra=false dns=false want=false exit=1.2.3.4 lan=false routes=[] nf=off update=off Persist=nil}`,
}, },
{ {
Prefs{ Prefs{
ExitNodeID: tailcfg.StableNodeID("myNodeABC"), ExitNodeID: tailcfg.StableNodeID("myNodeABC"),
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false exit=myNodeABC lan=false routes=[] nf=off update=off Persist=nil}`, `Prefs{ra=false dns=false want=false exit=myNodeABC lan=false routes=[] nf=off update=off Persist=nil}`,
}, },
{ {
Prefs{ Prefs{
@ -507,21 +492,21 @@ func TestPrefsPretty(t *testing.T) {
ExitNodeAllowLANAccess: true, ExitNodeAllowLANAccess: true,
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false exit=myNodeABC lan=true routes=[] nf=off update=off Persist=nil}`, `Prefs{ra=false dns=false want=false exit=myNodeABC lan=true routes=[] nf=off update=off Persist=nil}`,
}, },
{ {
Prefs{ Prefs{
ExitNodeAllowLANAccess: true, ExitNodeAllowLANAccess: true,
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off update=off Persist=nil}`, `Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist=nil}`,
}, },
{ {
Prefs{ Prefs{
Hostname: "foo", Hostname: "foo",
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off host="foo" update=off Persist=nil}`, `Prefs{ra=false dns=false want=false routes=[] nf=off host="foo" update=off Persist=nil}`,
}, },
{ {
Prefs{ Prefs{
@ -531,7 +516,7 @@ func TestPrefsPretty(t *testing.T) {
}, },
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off update=check Persist=nil}`, `Prefs{ra=false dns=false want=false routes=[] nf=off update=check Persist=nil}`,
}, },
{ {
Prefs{ Prefs{
@ -541,7 +526,7 @@ func TestPrefsPretty(t *testing.T) {
}, },
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off update=on Persist=nil}`, `Prefs{ra=false dns=false want=false routes=[] nf=off update=on Persist=nil}`,
}, },
{ {
Prefs{ Prefs{
@ -550,7 +535,7 @@ func TestPrefsPretty(t *testing.T) {
}, },
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off update=off appconnector=advertise Persist=nil}`, `Prefs{ra=false dns=false want=false routes=[] nf=off update=off appconnector=advertise Persist=nil}`,
}, },
{ {
Prefs{ Prefs{
@ -559,21 +544,21 @@ func TestPrefsPretty(t *testing.T) {
}, },
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off update=off Persist=nil}`, `Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist=nil}`,
}, },
{ {
Prefs{ Prefs{
NetfilterKind: "iptables", NetfilterKind: "iptables",
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off netfilterKind=iptables update=off Persist=nil}`, `Prefs{ra=false dns=false want=false routes=[] nf=off netfilterKind=iptables update=off Persist=nil}`,
}, },
{ {
Prefs{ Prefs{
NetfilterKind: "", NetfilterKind: "",
}, },
"linux", "linux",
`Prefs{ra=false mesh=false dns=false want=false routes=[] nf=off update=off Persist=nil}`, `Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist=nil}`,
}, },
} }
for i, tt := range tests { for i, tt := range tests {
@ -633,8 +618,9 @@ func TestMaskedPrefsSetsInternal(t *testing.T) {
func TestMaskedPrefsFields(t *testing.T) { func TestMaskedPrefsFields(t *testing.T) {
have := map[string]bool{} have := map[string]bool{}
for _, f := range fieldsOf(reflect.TypeFor[Prefs]()) { for _, f := range fieldsOf(reflect.TypeFor[Prefs]()) {
if f == "Persist" { switch f {
// This one can't be edited. case "Persist", "AllowSingleHosts":
// These can't be edited.
continue continue
} }
have[f] = true have[f] = true
@ -755,7 +741,6 @@ func TestMaskedPrefsPretty(t *testing.T) {
Prefs: Prefs{ Prefs: Prefs{
Hostname: "bar", Hostname: "bar",
OperatorUser: "galaxybrain", OperatorUser: "galaxybrain",
AllowSingleHosts: true,
RouteAll: false, RouteAll: false,
ExitNodeID: "foo", ExitNodeID: "foo",
AdvertiseTags: []string{"tag:foo", "tag:bar"}, AdvertiseTags: []string{"tag:foo", "tag:bar"},
@ -1064,3 +1049,24 @@ func TestNotifyPrefsJSONRoundtrip(t *testing.T) {
t.Fatal("Prefs should not be valid after deserialization") t.Fatal("Prefs should not be valid after deserialization")
} }
} }
// Verify that our Prefs type writes out an AllowSingleHosts field so we can
// downgrade to older versions that require it.
func TestPrefsDowngrade(t *testing.T) {
var p Prefs
j, err := json.Marshal(p)
if err != nil {
t.Fatal(err)
}
type oldPrefs struct {
AllowSingleHosts bool
}
var op oldPrefs
if err := json.Unmarshal(j, &op); err != nil {
t.Fatal(err)
}
if !op.AllowSingleHosts {
t.Fatal("AllowSingleHosts should be true")
}
}

@ -388,6 +388,6 @@ func (nm *NetworkMap) JSON() string {
type WGConfigFlags int type WGConfigFlags int
const ( const (
AllowSingleHosts WGConfigFlags = 1 << iota _ WGConfigFlags = 1 << iota
AllowSubnetRoutes AllowSubnetRoutes
) )

@ -331,7 +331,7 @@ func meshStacks(logf logger.Logf, mutateNetmap func(idx int, nm *netmap.NetworkM
peerSet.Add(peer.Key()) peerSet.Add(peer.Key())
} }
m.conn.UpdatePeers(peerSet) m.conn.UpdatePeers(peerSet)
wg, err := nmcfg.WGCfg(nm, logf, netmap.AllowSingleHosts, "") wg, err := nmcfg.WGCfg(nm, logf, 0, "")
if err != nil { if err != nil {
// We're too far from the *testing.T to be graceful, // We're too far from the *testing.T to be graceful,
// blow up. Shouldn't happen anyway. // blow up. Shouldn't happen anyway.
@ -2354,7 +2354,7 @@ func TestIsWireGuardOnlyPeer(t *testing.T) {
} }
m.conn.SetNetworkMap(nm) m.conn.SetNetworkMap(nm)
cfg, err := nmcfg.WGCfg(nm, t.Logf, netmap.AllowSingleHosts|netmap.AllowSubnetRoutes, "") cfg, err := nmcfg.WGCfg(nm, t.Logf, netmap.AllowSubnetRoutes, "")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -2416,7 +2416,7 @@ func TestIsWireGuardOnlyPeerWithMasquerade(t *testing.T) {
} }
m.conn.SetNetworkMap(nm) m.conn.SetNetworkMap(nm)
cfg, err := nmcfg.WGCfg(nm, t.Logf, netmap.AllowSingleHosts|netmap.AllowSubnetRoutes, "") cfg, err := nmcfg.WGCfg(nm, t.Logf, netmap.AllowSubnetRoutes, "")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -2456,7 +2456,7 @@ func applyNetworkMap(t *testing.T, m *magicStack, nm *netmap.NetworkMap) {
m.conn.noV6.Store(true) m.conn.noV6.Store(true)
// Turn the network map into a wireguard config (for the tailscale internal wireguard device). // Turn the network map into a wireguard config (for the tailscale internal wireguard device).
cfg, err := nmcfg.WGCfg(nm, t.Logf, netmap.AllowSingleHosts|netmap.AllowSubnetRoutes, "") cfg, err := nmcfg.WGCfg(nm, t.Logf, netmap.AllowSubnetRoutes, "")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -10,7 +10,6 @@ import (
"net/netip" "net/netip"
"strings" "strings"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/logid" "tailscale.com/types/logid"
@ -124,12 +123,6 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags,
} }
fmt.Fprintf(skippedUnselected, "%q (%v)", nodeDebugName(peer), peer.Key().ShortString()) fmt.Fprintf(skippedUnselected, "%q (%v)", nodeDebugName(peer), peer.Key().ShortString())
continue continue
} else if allowedIP.IsSingleIP() && tsaddr.IsTailscaleIP(allowedIP.Addr()) && (flags&netmap.AllowSingleHosts) == 0 {
if skippedIPs.Len() > 0 {
skippedIPs.WriteString(", ")
}
fmt.Fprintf(skippedIPs, "%v from %q (%v)", allowedIP.Addr(), nodeDebugName(peer), peer.Key().ShortString())
continue
} else if cidrIsSubnet(peer, allowedIP) { } else if cidrIsSubnet(peer, allowedIP) {
if (flags & netmap.AllowSubnetRoutes) == 0 { if (flags & netmap.AllowSubnetRoutes) == 0 {
if skippedSubnets.Len() > 0 { if skippedSubnets.Len() > 0 {

Loading…
Cancel
Save