cmd/tailscale: switch to an ffcli based CLI.

Two commands for now, `up` and `netcheck`. The commands and the flags they take
will change a bunch in the future, but this is good enough to launch on parity
with relaynode.

Signed-Off-By: David Anderson <dave@natulte.net>
pull/115/head
David Anderson 5 years ago committed by Dave Anderson
parent 4e3ff4b63b
commit 78654ee1bd

@ -11,16 +11,11 @@ import (
"sort" "sort"
"time" "time"
"github.com/pborman/getopt/v2"
"tailscale.com/netcheck" "tailscale.com/netcheck"
) )
func isSubcommand(cmd string) bool { func runNetcheck(ctx context.Context, args []string) error {
return len(getopt.Args()) == 1 && getopt.Args()[0] == cmd ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
}
func netcheckCmd() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
report, err := netcheck.GetReport(ctx, log.Printf) report, err := netcheck.GetReport(ctx, log.Printf)
if err != nil { if err != nil {
@ -39,4 +34,5 @@ func netcheckCmd() {
for _, s := range ss { for _, s := range ss {
fmt.Printf("\t\t- %s = %v\n", s, report.DERPLatency[s]) fmt.Printf("\t\t- %s = %v\n", s, report.DERPLatency[s])
} }
return nil
} }

@ -8,15 +8,18 @@ package main // import "tailscale.com/cmd/tailscale"
import ( import (
"context" "context"
"flag"
"fmt" "fmt"
"log" "log"
"net" "net"
"os" "os"
"os/signal" "os/signal"
"strings"
"syscall" "syscall"
"github.com/apenwarr/fixconsole" "github.com/apenwarr/fixconsole"
"github.com/pborman/getopt/v2" "github.com/pborman/getopt/v2"
"github.com/peterbourgon/ff/v2/ffcli"
"github.com/tailscale/wireguard-go/wgcfg" "github.com/tailscale/wireguard-go/wgcfg"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/logpolicy" "tailscale.com/logpolicy"
@ -52,45 +55,98 @@ func main() {
log.Printf("fixConsoleOutput: %v\n", err) log.Printf("fixConsoleOutput: %v\n", err)
} }
socket := getopt.StringLong("socket", 0, "/run/tailscale/tailscaled.sock", "path of tailscaled's unix socket") upf := flag.NewFlagSet("up", flag.ExitOnError)
server := getopt.StringLong("server", 's', "https://login.tailscale.com", "URL to tailcontrol server") upf.StringVar(&upArgs.socket, "socket", "/run/tailscale/tailscaled.sock", "path to tailscaled's unix socket")
nuroutes := getopt.BoolLong("no-single-routes", 'N', "disallow (non-subnet) routes to single nodes") upf.StringVar(&upArgs.server, "login-server", "https://login.tailscale.com", "base URL of control server")
routeall := getopt.BoolLong("remote-routes", 'R', "accept routes advertised by remote nodes") upf.BoolVar(&upArgs.acceptRoutes, "accept-routes", false, "accept routes advertised by other Tailscale nodes")
nopf := getopt.BoolLong("no-packet-filter", 'F', "disable packet filter") upf.BoolVar(&upArgs.noSingleRoutes, "no-single-routes", false, "don't install routes to single nodes")
advroutes := getopt.ListLong("routes", 'r', "routes to advertise to other nodes (comma-separated, e.g. 10.0.0.0/8,192.168.1.0/24)") upf.BoolVar(&upArgs.noPacketFilter, "no-packet-filter", false, "disable packet filter")
getopt.Parse() upf.StringVar(&upArgs.advertiseRoutes, "advertise-routes", "", "routes to advertise to other nodes (comma-separated, e.g. 10.0.0.0/8,192.168.0.0/24)")
// TODO(bradfitz): move this into a proper subcommand system when we have that. upCmd := &ffcli.Command{
if isSubcommand("netcheck") { Name: "up",
netcheckCmd() ShortUsage: "up [flags]",
return ShortHelp: "Connect to your Tailscale network",
LongHelp: strings.TrimSpace(`
"tailscale up" connects this machine to your Tailscale network,
triggering authentication if necessary.
The flags passed to this command set tailscaled options that are
specific to this machine, such as whether to advertise some routes to
other nodes in the Tailscale network. If you don't specify any flags,
options are reset to their default.
`),
FlagSet: upf,
Exec: runUp,
}
netcheckCmd := &ffcli.Command{
Name: "netcheck",
ShortUsage: "netcheck",
ShortHelp: "Print an analysis of local network conditions",
Exec: runNetcheck,
}
rootCmd := &ffcli.Command{
Name: "tailscale",
ShortUsage: "tailscale subcommand [flags]",
ShortHelp: "The easiest, most secure way to use WireGuard.",
LongHelp: strings.TrimSpace(`
This CLI is still under active development. Commands and flags will
change in the future.
`),
Subcommands: []*ffcli.Command{
upCmd,
netcheckCmd,
},
Exec: func(context.Context, []string) error { return flag.ErrHelp },
} }
if err := rootCmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil && err != flag.ErrHelp {
log.Fatal(err)
}
}
var upArgs = struct {
socket string
server string
acceptRoutes bool
noSingleRoutes bool
noPacketFilter bool
advertiseRoutes string
}{}
func runUp(ctx context.Context, args []string) error {
pol := logpolicy.New("tailnode.log.tailscale.io") pol := logpolicy.New("tailnode.log.tailscale.io")
if len(getopt.Args()) > 0 { if len(args) > 0 {
log.Fatalf("too many non-flag arguments: %#v", getopt.Args()[0]) log.Fatalf("too many non-flag arguments: %#v", getopt.Args()[0])
} }
defer pol.Close() defer pol.Close()
var adv []wgcfg.CIDR var adv []wgcfg.CIDR
for _, s := range *advroutes { if upArgs.advertiseRoutes != "" {
advroutes := strings.Split(upArgs.advertiseRoutes, ",")
for _, s := range advroutes {
cidr, err := wgcfg.ParseCIDR(s) cidr, err := wgcfg.ParseCIDR(s)
if err != nil { if err != nil {
log.Fatalf("%q is not a valid CIDR prefix: %v", s, err) log.Fatalf("%q is not a valid CIDR prefix: %v", s, err)
} }
adv = append(adv, *cidr) adv = append(adv, *cidr)
} }
}
// TODO(apenwarr): fix different semantics between prefs and uflags // TODO(apenwarr): fix different semantics between prefs and uflags
// TODO(apenwarr): allow setting/using CorpDNS // TODO(apenwarr): allow setting/using CorpDNS
prefs := ipn.NewPrefs() prefs := ipn.NewPrefs()
prefs.ControlURL = *server prefs.ControlURL = upArgs.server
prefs.WantRunning = true prefs.WantRunning = true
prefs.RouteAll = *routeall prefs.RouteAll = upArgs.acceptRoutes
prefs.AllowSingleHosts = !*nuroutes prefs.AllowSingleHosts = !upArgs.noSingleRoutes
prefs.UsePacketFilter = !*nopf prefs.UsePacketFilter = !upArgs.noPacketFilter
prefs.AdvertiseRoutes = adv prefs.AdvertiseRoutes = adv
c, err := safesocket.Connect(*socket, 0) c, err := safesocket.Connect(upArgs.socket, 0)
if err != nil { if err != nil {
log.Fatalf("safesocket.Connect: %v\n", err) log.Fatalf("safesocket.Connect: %v\n", err)
} }
@ -98,7 +154,7 @@ func main() {
ipn.WriteMsg(c, b) ipn.WriteMsg(c, b)
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
go func() { go func() {
@ -121,7 +177,7 @@ func main() {
case ipn.NeedsLogin: case ipn.NeedsLogin:
bc.StartLoginInteractive() bc.StartLoginInteractive()
case ipn.NeedsMachineAuth: case ipn.NeedsMachineAuth:
fmt.Fprintf(os.Stderr, "\nTo authorize your machine, visit (as admin):\n\n\t%s/admin/machines\n\n", *server) fmt.Fprintf(os.Stderr, "\nTo authorize your machine, visit (as admin):\n\n\t%s/admin/machines\n\n", upArgs.server)
case ipn.Starting, ipn.Running: case ipn.Starting, ipn.Running:
// Done full authentication process // Done full authentication process
fmt.Fprintf(os.Stderr, "\ntailscaled is authenticated, nothing more to do.\n\n") fmt.Fprintf(os.Stderr, "\ntailscaled is authenticated, nothing more to do.\n\n")
@ -142,4 +198,6 @@ func main() {
// Windows/Mac state is moved into backend. // Windows/Mac state is moved into backend.
bc.Start(opts) bc.Start(opts)
pump(ctx, bc, c) pump(ctx, bc, c)
return nil
} }

@ -15,6 +15,8 @@ require (
github.com/kr/pty v1.1.1 github.com/kr/pty v1.1.1
github.com/mdlayher/netlink v1.1.0 github.com/mdlayher/netlink v1.1.0
github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3 github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3
github.com/peterbourgon/ff v1.7.0
github.com/peterbourgon/ff/v2 v2.0.0
github.com/tailscale/hujson v0.0.0-20190930033718-5098e564d9b3 // indirect github.com/tailscale/hujson v0.0.0-20190930033718-5098e564d9b3 // indirect
github.com/tailscale/winipcfg-go v0.0.0-20200213045944-185b07f8233f github.com/tailscale/winipcfg-go v0.0.0-20200213045944-185b07f8233f
github.com/tailscale/wireguard-go v0.0.0-20200225215529-3ec48fad1002 github.com/tailscale/wireguard-go v0.0.0-20200225215529-3ec48fad1002
@ -27,3 +29,4 @@ require (
honnef.co/go/tools v0.0.1-2019.2.3 // indirect honnef.co/go/tools v0.0.1-2019.2.3 // indirect
rsc.io/goversion v1.2.0 rsc.io/goversion v1.2.0
) )

@ -61,10 +61,16 @@ github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0 h1:mpdLgm+brq10nI9zM1BpX1kpDbh3NLl3RSnVq6ZSkfg= github.com/mdlayher/netlink v1.1.0 h1:mpdLgm+brq10nI9zM1BpX1kpDbh3NLl3RSnVq6ZSkfg=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3 h1:YtFkrqsMEj7YqpIhRteVxJxCeC3jJBieuLr0d4C4rSA= github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3 h1:YtFkrqsMEj7YqpIhRteVxJxCeC3jJBieuLr0d4C4rSA=
github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
github.com/peterbourgon/ff v1.7.0 h1:hknvTgsh90jNBIjPq7xeq32Y9AmSbpXvjrFW4sJwW+A=
github.com/peterbourgon/ff v1.7.0/go.mod h1:/KKxnU5cBj4w21jEMj4Rway/kslRP6XAOHh7CH8AyAM=
github.com/peterbourgon/ff/v2 v2.0.0 h1:lx0oYI5qr/FU1xnpNhQ+EZM04gKgn46jyYvGEEqBBbY=
github.com/peterbourgon/ff/v2 v2.0.0/go.mod h1:xjwr+t+SjWm4L46fcj/D+Ap+6ME7+HqFzaP22pP5Ggk=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -150,6 +156,7 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac h1:MQEvx39qSf8vyrx3XRaOe+j1UDIzKwkYOVObRgGPVqI= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac h1:MQEvx39qSf8vyrx3XRaOe+j1UDIzKwkYOVObRgGPVqI=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8= golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8=
@ -162,6 +169,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gortc.io/stun v1.22.1 h1:96mOdDATYRqhYB+TZdenWBg4CzL2Ye5kPyBXQ8KAB+8= gortc.io/stun v1.22.1 h1:96mOdDATYRqhYB+TZdenWBg4CzL2Ye5kPyBXQ8KAB+8=

Loading…
Cancel
Save