From 0834712c918a6df8a7719e84bf98b9e92b13bb7c Mon Sep 17 00:00:00 2001 From: Adrian Dewhurst Date: Mon, 15 Jul 2024 11:13:11 -0400 Subject: [PATCH] ipn: allow FQDN in exit node selection To match the format of exit node suggestions and ensure that the result is not ambiguous, relax exit node CLI selection to permit using a FQDN including the trailing dot. Updates #12618 Change-Id: I04b9b36d2743154aa42f2789149b2733f8555d3f Signed-off-by: Adrian Dewhurst --- cmd/tailscale/cli/exitnode.go | 3 +-- ipn/prefs.go | 2 +- ipn/prefs_test.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/cmd/tailscale/cli/exitnode.go b/cmd/tailscale/cli/exitnode.go index 9490b4de9..6b9247a7b 100644 --- a/cmd/tailscale/cli/exitnode.go +++ b/cmd/tailscale/cli/exitnode.go @@ -156,8 +156,7 @@ func runExitNodeSuggest(ctx context.Context, args []string) error { fmt.Println("No exit node suggestion is available.") return nil } - hostname := strings.TrimSuffix(res.Name, ".") - fmt.Printf("Suggested exit node: %v\nTo accept this suggestion, use `tailscale set --exit-node=%v`.\n", hostname, shellquote.Join(hostname)) + fmt.Printf("Suggested exit node: %v\nTo accept this suggestion, use `tailscale set --exit-node=%v`.\n", res.Name, shellquote.Join(res.Name)) return nil } diff --git a/ipn/prefs.go b/ipn/prefs.go index f3c9758ac..5d61f0119 100644 --- a/ipn/prefs.go +++ b/ipn/prefs.go @@ -810,7 +810,7 @@ func exitNodeIPOfArg(s string, st *ipnstate.Status) (ip netip.Addr, err error) { match := 0 for _, ps := range st.Peer { baseName := dnsname.TrimSuffix(ps.DNSName, st.MagicDNSSuffix) - if !strings.EqualFold(s, baseName) { + if !strings.EqualFold(s, baseName) && !strings.EqualFold(s, ps.DNSName) { continue } match++ diff --git a/ipn/prefs_test.go b/ipn/prefs_test.go index 65aa80d04..dcb999ef5 100644 --- a/ipn/prefs_test.go +++ b/ipn/prefs_test.go @@ -914,6 +914,21 @@ func TestExitNodeIPOfArg(t *testing.T) { }, want: mustIP("1.0.0.2"), }, + { + name: "name_fqdn", + arg: "skippy.foo.", + st: &ipnstate.Status{ + MagicDNSSuffix: ".foo", + Peer: map[key.NodePublic]*ipnstate.PeerStatus{ + key.NewNode().Public(): { + DNSName: "skippy.foo.", + TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")}, + ExitNodeOption: true, + }, + }, + }, + want: mustIP("1.0.0.2"), + }, { name: "name_not_exit", arg: "skippy", @@ -928,6 +943,20 @@ func TestExitNodeIPOfArg(t *testing.T) { }, wantErr: `node "skippy" is not advertising an exit node`, }, + { + name: "name_wrong_fqdn", + arg: "skippy.bar.", + st: &ipnstate.Status{ + MagicDNSSuffix: ".foo", + Peer: map[key.NodePublic]*ipnstate.PeerStatus{ + key.NewNode().Public(): { + DNSName: "skippy.foo.", + TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")}, + }, + }, + }, + wantErr: `invalid value "skippy.bar." for --exit-node; must be IP or unique node name`, + }, { name: "ambiguous", arg: "skippy",