cmd/tailscale: don't allow setting exit node to known invalid value
Fixes #3081 Change-Id: I34c378dfd51ee013c21962dbe79c49b1ba06c0c5 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>pull/3489/head
parent
a8f60cf6e8
commit
03a323de4e
|
@ -852,9 +852,47 @@ func TestExitNodeIPOfArg(t *testing.T) {
|
||||||
want netaddr.IP
|
want netaddr.IP
|
||||||
wantErr string
|
wantErr string
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "ip_while_stopped_okay",
|
||||||
|
arg: "1.2.3.4",
|
||||||
|
st: &ipnstate.Status{
|
||||||
|
BackendState: "Stopped",
|
||||||
|
},
|
||||||
|
want: mustIP("1.2.3.4"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ip_not_found",
|
||||||
|
arg: "1.2.3.4",
|
||||||
|
st: &ipnstate.Status{
|
||||||
|
BackendState: "Running",
|
||||||
|
},
|
||||||
|
wantErr: `no node found in netmap with IP 1.2.3.4`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ip_not_exit",
|
||||||
|
arg: "1.2.3.4",
|
||||||
|
st: &ipnstate.Status{
|
||||||
|
BackendState: "Running",
|
||||||
|
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
|
||||||
|
key.NewNode().Public(): {
|
||||||
|
TailscaleIPs: []netaddr.IP{mustIP("1.2.3.4")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: `node 1.2.3.4 is not advertising an exit node`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ip",
|
name: "ip",
|
||||||
arg: "1.2.3.4",
|
arg: "1.2.3.4",
|
||||||
|
st: &ipnstate.Status{
|
||||||
|
BackendState: "Running",
|
||||||
|
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
|
||||||
|
key.NewNode().Public(): {
|
||||||
|
TailscaleIPs: []netaddr.IP{mustIP("1.2.3.4")},
|
||||||
|
ExitNodeOption: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
want: mustIP("1.2.3.4"),
|
want: mustIP("1.2.3.4"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -872,11 +910,26 @@ func TestExitNodeIPOfArg(t *testing.T) {
|
||||||
key.NewNode().Public(): {
|
key.NewNode().Public(): {
|
||||||
DNSName: "skippy.foo.",
|
DNSName: "skippy.foo.",
|
||||||
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
|
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
|
||||||
|
ExitNodeOption: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: mustIP("1.0.0.2"),
|
want: mustIP("1.0.0.2"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "name_not_exit",
|
||||||
|
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")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: `node "skippy" is not advertising an exit node`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ambiguous",
|
name: "ambiguous",
|
||||||
arg: "skippy",
|
arg: "skippy",
|
||||||
|
@ -886,10 +939,12 @@ func TestExitNodeIPOfArg(t *testing.T) {
|
||||||
key.NewNode().Public(): {
|
key.NewNode().Public(): {
|
||||||
DNSName: "skippy.foo.",
|
DNSName: "skippy.foo.",
|
||||||
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
|
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
|
||||||
|
ExitNodeOption: true,
|
||||||
},
|
},
|
||||||
key.NewNode().Public(): {
|
key.NewNode().Public(): {
|
||||||
DNSName: "SKIPPY.foo.",
|
DNSName: "SKIPPY.foo.",
|
||||||
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
|
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
|
||||||
|
ExitNodeOption: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -193,12 +193,38 @@ func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]
|
||||||
return routes, nil
|
return routes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// peerWithTailscaleIP returns the peer in st with the provided
|
||||||
|
// Tailscale IP.
|
||||||
|
func peerWithTailscaleIP(st *ipnstate.Status, ip netaddr.IP) (ps *ipnstate.PeerStatus, ok bool) {
|
||||||
|
for _, ps := range st.Peer {
|
||||||
|
for _, ip2 := range ps.TailscaleIPs {
|
||||||
|
if ip == ip2 {
|
||||||
|
return ps, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// exitNodeIPOfArg maps from a user-provided CLI flag value to an IP
|
||||||
|
// address they want to use as an exit node.
|
||||||
func exitNodeIPOfArg(arg string, st *ipnstate.Status) (ip netaddr.IP, err error) {
|
func exitNodeIPOfArg(arg string, st *ipnstate.Status) (ip netaddr.IP, err error) {
|
||||||
if arg == "" {
|
if arg == "" {
|
||||||
return ip, errors.New("invalid use of exitNodeIPOfArg with empty string")
|
return ip, errors.New("invalid use of exitNodeIPOfArg with empty string")
|
||||||
}
|
}
|
||||||
ip, err = netaddr.ParseIP(arg)
|
ip, err = netaddr.ParseIP(arg)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
// If we're online already and have a netmap, double check that the IP
|
||||||
|
// address specified is valid.
|
||||||
|
if st.BackendState == "Running" {
|
||||||
|
ps, ok := peerWithTailscaleIP(st, ip)
|
||||||
|
if !ok {
|
||||||
|
return ip, fmt.Errorf("no node found in netmap with IP %v", ip)
|
||||||
|
}
|
||||||
|
if !ps.ExitNodeOption {
|
||||||
|
return ip, fmt.Errorf("node %v is not advertising an exit node", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
return ip, err
|
return ip, err
|
||||||
}
|
}
|
||||||
match := 0
|
match := 0
|
||||||
|
@ -211,6 +237,9 @@ func exitNodeIPOfArg(arg string, st *ipnstate.Status) (ip netaddr.IP, err error)
|
||||||
if len(ps.TailscaleIPs) == 0 {
|
if len(ps.TailscaleIPs) == 0 {
|
||||||
return ip, fmt.Errorf("node %q has no Tailscale IP?", arg)
|
return ip, fmt.Errorf("node %q has no Tailscale IP?", arg)
|
||||||
}
|
}
|
||||||
|
if !ps.ExitNodeOption {
|
||||||
|
return ip, fmt.Errorf("node %q is not advertising an exit node", arg)
|
||||||
|
}
|
||||||
ip = ps.TailscaleIPs[0]
|
ip = ps.TailscaleIPs[0]
|
||||||
}
|
}
|
||||||
switch match {
|
switch match {
|
||||||
|
|
Loading…
Reference in New Issue