mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-07 08:07:42 +00:00
cmd/tailscale: let --exit-node= take a machine name in addition to IP
If you're online, let tailscale up --exit-node=NAME map NAME to its IP. We don't store the exit node name server-side in prefs, avoiding the concern raised earlier. Fixes #3062 Change-Id: Ieea5ceec1a30befc67e9d6b8a530b3cb047b6b40 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
adc5997592
commit
f91481075d
@ -18,6 +18,7 @@
|
|||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/tstest"
|
"tailscale.com/tstest"
|
||||||
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/persist"
|
"tailscale.com/types/persist"
|
||||||
"tailscale.com/types/preftype"
|
"tailscale.com/types/preftype"
|
||||||
"tailscale.com/version/distro"
|
"tailscale.com/version/distro"
|
||||||
@ -567,7 +568,7 @@ func TestPrefsFromUpArgs(t *testing.T) {
|
|||||||
args: upArgsT{
|
args: upArgsT{
|
||||||
exitNodeIP: "foo",
|
exitNodeIP: "foo",
|
||||||
},
|
},
|
||||||
wantErr: `invalid IP address "foo" for --exit-node: ParseIP("foo"): unable to parse IP`,
|
wantErr: `invalid value "foo" for --exit-node; must be IP or unique node name`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error_exit_node_allow_lan_without_exit_node",
|
name: "error_exit_node_allow_lan_without_exit_node",
|
||||||
@ -841,3 +842,78 @@ func TestUpdatePrefs(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExitNodeIPOfArg(t *testing.T) {
|
||||||
|
mustIP := netaddr.MustParseIP
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
arg string
|
||||||
|
st *ipnstate.Status
|
||||||
|
want netaddr.IP
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ip",
|
||||||
|
arg: "1.2.3.4",
|
||||||
|
want: mustIP("1.2.3.4"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no_match",
|
||||||
|
arg: "unknown",
|
||||||
|
st: &ipnstate.Status{MagicDNSSuffix: ".foo"},
|
||||||
|
wantErr: `invalid value "unknown" for --exit-node; must be IP or unique node name`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
arg: "skippy",
|
||||||
|
st: &ipnstate.Status{
|
||||||
|
MagicDNSSuffix: ".foo",
|
||||||
|
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
|
||||||
|
key.NewNode().Public(): {
|
||||||
|
DNSName: "skippy.foo.",
|
||||||
|
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: mustIP("1.0.0.2"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ambiguous",
|
||||||
|
arg: "skippy",
|
||||||
|
st: &ipnstate.Status{
|
||||||
|
MagicDNSSuffix: ".foo",
|
||||||
|
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
|
||||||
|
key.NewNode().Public(): {
|
||||||
|
DNSName: "skippy.foo.",
|
||||||
|
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
|
||||||
|
},
|
||||||
|
key.NewNode().Public(): {
|
||||||
|
DNSName: "SKIPPY.foo.",
|
||||||
|
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: `ambiguous exit node name "skippy"`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := exitNodeIPOfArg(tt.arg, tt.st)
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == tt.wantErr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tt.wantErr == "" {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Fatalf("error = %#q; want %#q", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
t.Fatalf("got %v; want error %#q", got, tt.wantErr)
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Fatalf("got %v; want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/preftype"
|
"tailscale.com/types/preftype"
|
||||||
|
"tailscale.com/util/dnsname"
|
||||||
"tailscale.com/version/distro"
|
"tailscale.com/version/distro"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ func newUpFlagSet(goos string, upArgs *upArgsT) *flag.FlagSet {
|
|||||||
upf.BoolVar(&upArgs.acceptRoutes, "accept-routes", false, "accept routes advertised by other Tailscale nodes")
|
upf.BoolVar(&upArgs.acceptRoutes, "accept-routes", false, "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, "install host routes to other Tailscale nodes")
|
upf.BoolVar(&upArgs.singleRoutes, "host-routes", true, "install host routes to other Tailscale nodes")
|
||||||
upf.StringVar(&upArgs.exitNodeIP, "exit-node", "", "Tailscale IP of the exit node 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")
|
||||||
upf.StringVar(&upArgs.advertiseTags, "advertise-tags", "", "comma-separated ACL tags to request; each must start with \"tag:\" (e.g. \"tag:eng,tag:montreal,tag:ssh\")")
|
upf.StringVar(&upArgs.advertiseTags, "advertise-tags", "", "comma-separated ACL tags to request; each must start with \"tag:\" (e.g. \"tag:eng,tag:montreal,tag:ssh\")")
|
||||||
@ -190,6 +191,36 @@ func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]
|
|||||||
return routes, nil
|
return routes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func exitNodeIPOfArg(arg string, st *ipnstate.Status) (ip netaddr.IP, err error) {
|
||||||
|
if arg == "" {
|
||||||
|
return ip, errors.New("invalid use of exitNodeIPOfArg with empty string")
|
||||||
|
}
|
||||||
|
ip, err = netaddr.ParseIP(arg)
|
||||||
|
if err == nil {
|
||||||
|
return ip, err
|
||||||
|
}
|
||||||
|
match := 0
|
||||||
|
for _, ps := range st.Peer {
|
||||||
|
baseName := dnsname.TrimSuffix(ps.DNSName, st.MagicDNSSuffix)
|
||||||
|
if !strings.EqualFold(arg, baseName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
match++
|
||||||
|
if len(ps.TailscaleIPs) == 0 {
|
||||||
|
return ip, fmt.Errorf("node %q has no Tailscale IP?", arg)
|
||||||
|
}
|
||||||
|
ip = ps.TailscaleIPs[0]
|
||||||
|
}
|
||||||
|
switch match {
|
||||||
|
case 0:
|
||||||
|
return ip, fmt.Errorf("invalid value %q for --exit-node; must be IP or unique node name", arg)
|
||||||
|
case 1:
|
||||||
|
return ip, nil
|
||||||
|
default:
|
||||||
|
return ip, fmt.Errorf("ambiguous exit node name %q", arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// prefsFromUpArgs returns the ipn.Prefs for the provided args.
|
// prefsFromUpArgs returns the ipn.Prefs for the provided args.
|
||||||
//
|
//
|
||||||
// Note that the parameters upArgs and warnf are named intentionally
|
// Note that the parameters upArgs and warnf are named intentionally
|
||||||
@ -205,9 +236,9 @@ func prefsFromUpArgs(upArgs upArgsT, warnf logger.Logf, st *ipnstate.Status, goo
|
|||||||
var exitNodeIP netaddr.IP
|
var exitNodeIP netaddr.IP
|
||||||
if upArgs.exitNodeIP != "" {
|
if upArgs.exitNodeIP != "" {
|
||||||
var err error
|
var err error
|
||||||
exitNodeIP, err = netaddr.ParseIP(upArgs.exitNodeIP)
|
exitNodeIP, err = exitNodeIPOfArg(upArgs.exitNodeIP, st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid IP address %q for --exit-node: %v", upArgs.exitNodeIP, err)
|
return nil, err
|
||||||
}
|
}
|
||||||
} else if upArgs.exitNodeAllowLANAccess {
|
} else if upArgs.exitNodeAllowLANAccess {
|
||||||
return nil, fmt.Errorf("--exit-node-allow-lan-access can only be used with --exit-node")
|
return nil, fmt.Errorf("--exit-node-allow-lan-access can only be used with --exit-node")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user