mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
cmd/tailscale/cli: add login and switch subcommands
Updates #713 Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
parent
ec1e67b1ab
commit
f3519f7b29
@ -903,3 +903,43 @@ func (r jsonReader) Read(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
return r.b.Read(p)
|
return r.b.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProfileStatus returns the current profile and the list of all profiles.
|
||||||
|
func (lc *LocalClient) ProfileStatus(ctx context.Context) (current ipn.LoginProfile, all []ipn.LoginProfile, err error) {
|
||||||
|
body, err := lc.send(ctx, "GET", "/localapi/v0/profiles/current", 200, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
current, err = decodeJSON[ipn.LoginProfile](body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body, err = lc.send(ctx, "GET", "/localapi/v0/profiles/", 200, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
all, err = decodeJSON[[]ipn.LoginProfile](body)
|
||||||
|
return current, all, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwitchToEmptyProfile creates and switches to a new unnamed profile. The new
|
||||||
|
// profile is not assigned an ID until it is persisted after a successful login.
|
||||||
|
// In order to login to the new profile, the user must call LoginInteractive.
|
||||||
|
func (lc *LocalClient) SwitchToEmptyProfile(ctx context.Context) error {
|
||||||
|
_, err := lc.send(ctx, "PUT", "/localapi/v0/profiles/", http.StatusCreated, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwitchProfile switches to the given profile.
|
||||||
|
func (lc *LocalClient) SwitchProfile(ctx context.Context, profile ipn.ProfileID) error {
|
||||||
|
_, err := lc.send(ctx, "POST", "/localapi/v0/profiles/"+url.PathEscape(string(profile)), 204, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteProfile removes the profile with the given ID.
|
||||||
|
// If the profile is the current profile, an empty profile
|
||||||
|
// will be selected as if SwitchToEmptyProfile was called.
|
||||||
|
func (lc *LocalClient) DeleteProfile(ctx context.Context, profile ipn.ProfileID) error {
|
||||||
|
_, err := lc.send(ctx, "DELETE", "/localapi/v0/profiles"+url.PathEscape(string(profile)), http.StatusNoContent, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/peterbourgon/ff/v3/ffcli"
|
"github.com/peterbourgon/ff/v3/ffcli"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"tailscale.com/client/tailscale"
|
"tailscale.com/client/tailscale"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
@ -35,6 +36,10 @@
|
|||||||
var Stderr io.Writer = os.Stderr
|
var Stderr io.Writer = os.Stderr
|
||||||
var Stdout io.Writer = os.Stdout
|
var Stdout io.Writer = os.Stdout
|
||||||
|
|
||||||
|
func errf(format string, a ...any) {
|
||||||
|
fmt.Fprintf(Stderr, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
func printf(format string, a ...any) {
|
func printf(format string, a ...any) {
|
||||||
fmt.Fprintf(Stdout, format, a...)
|
fmt.Fprintf(Stdout, format, a...)
|
||||||
}
|
}
|
||||||
@ -183,13 +188,20 @@ func Run(args []string) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if envknob.UseWIPCode() {
|
if envknob.UseWIPCode() {
|
||||||
rootCmd.Subcommands = append(rootCmd.Subcommands, idTokenCmd)
|
rootCmd.Subcommands = append(rootCmd.Subcommands,
|
||||||
rootCmd.Subcommands = append(rootCmd.Subcommands, serveCmd)
|
idTokenCmd,
|
||||||
|
serveCmd,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't advertise the debug command, but it exists.
|
// Don't advertise the debug command, but it exists.
|
||||||
if strSliceContains(args, "debug") {
|
switch {
|
||||||
|
case slices.Contains(args, "debug"):
|
||||||
rootCmd.Subcommands = append(rootCmd.Subcommands, debugCmd)
|
rootCmd.Subcommands = append(rootCmd.Subcommands, debugCmd)
|
||||||
|
case slices.Contains(args, "login"):
|
||||||
|
rootCmd.Subcommands = append(rootCmd.Subcommands, loginCmd)
|
||||||
|
case slices.Contains(args, "switch"):
|
||||||
|
rootCmd.Subcommands = append(rootCmd.Subcommands, switchCmd)
|
||||||
}
|
}
|
||||||
if runtime.GOOS == "linux" && distro.Get() == distro.Synology {
|
if runtime.GOOS == "linux" && distro.Get() == distro.Synology {
|
||||||
rootCmd.Subcommands = append(rootCmd.Subcommands, configureHostCmd)
|
rootCmd.Subcommands = append(rootCmd.Subcommands, configureHostCmd)
|
||||||
@ -287,15 +299,6 @@ func pump(ctx context.Context, bc *ipn.BackendClient, conn net.Conn) error {
|
|||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func strSliceContains(ss []string, s string) bool {
|
|
||||||
for _, v := range ss {
|
|
||||||
if v == s {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// usageFuncNoDefaultValues is like usageFunc but doesn't print default values.
|
// usageFuncNoDefaultValues is like usageFunc but doesn't print default values.
|
||||||
func usageFuncNoDefaultValues(c *ffcli.Command) string {
|
func usageFuncNoDefaultValues(c *ffcli.Command) string {
|
||||||
return usageFuncOpt(c, false)
|
return usageFuncOpt(c, false)
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
func TestUpdateMaskedPrefsFromUpFlag(t *testing.T) {
|
func TestUpdateMaskedPrefsFromUpFlag(t *testing.T) {
|
||||||
for _, goos := range geese {
|
for _, goos := range geese {
|
||||||
var upArgs upArgsT
|
var upArgs upArgsT
|
||||||
fs := newUpFlagSet(goos, &upArgs)
|
fs := newUpFlagSet(goos, &upArgs, "up")
|
||||||
fs.VisitAll(func(f *flag.Flag) {
|
fs.VisitAll(func(f *flag.Flag) {
|
||||||
mp := new(ipn.MaskedPrefs)
|
mp := new(ipn.MaskedPrefs)
|
||||||
updateMaskedPrefsFromUpOrSetFlag(mp, f.Name)
|
updateMaskedPrefsFromUpOrSetFlag(mp, f.Name)
|
||||||
@ -488,7 +488,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
|
|||||||
goos = tt.goos
|
goos = tt.goos
|
||||||
}
|
}
|
||||||
var upArgs upArgsT
|
var upArgs upArgsT
|
||||||
flagSet := newUpFlagSet(goos, &upArgs)
|
flagSet := newUpFlagSet(goos, &upArgs, "up")
|
||||||
flags := CleanUpArgs(tt.flags)
|
flags := CleanUpArgs(tt.flags)
|
||||||
flagSet.Parse(flags)
|
flagSet.Parse(flags)
|
||||||
newPrefs, err := prefsFromUpArgs(upArgs, t.Logf, new(ipnstate.Status), goos)
|
newPrefs, err := prefsFromUpArgs(upArgs, t.Logf, new(ipnstate.Status), goos)
|
||||||
@ -515,7 +515,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func upArgsFromOSArgs(goos string, flagArgs ...string) (args upArgsT) {
|
func upArgsFromOSArgs(goos string, flagArgs ...string) (args upArgsT) {
|
||||||
fs := newUpFlagSet(goos, &args)
|
fs := newUpFlagSet(goos, &args, "up")
|
||||||
fs.Parse(flagArgs) // populates args
|
fs.Parse(flagArgs) // populates args
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -775,7 +775,7 @@ func TestPrefFlagMapping(t *testing.T) {
|
|||||||
func TestFlagAppliesToOS(t *testing.T) {
|
func TestFlagAppliesToOS(t *testing.T) {
|
||||||
for _, goos := range geese {
|
for _, goos := range geese {
|
||||||
var upArgs upArgsT
|
var upArgs upArgsT
|
||||||
fs := newUpFlagSet(goos, &upArgs)
|
fs := newUpFlagSet(goos, &upArgs, "up")
|
||||||
fs.VisitAll(func(f *flag.Flag) {
|
fs.VisitAll(func(f *flag.Flag) {
|
||||||
if !flagAppliesToOS(f.Name, goos) {
|
if !flagAppliesToOS(f.Name, goos) {
|
||||||
t.Errorf("flagAppliesToOS(%q, %q) = false but found in %s set", f.Name, goos, goos)
|
t.Errorf("flagAppliesToOS(%q, %q) = false but found in %s set", f.Name, goos, goos)
|
||||||
@ -1070,7 +1070,7 @@ func TestUpdatePrefs(t *testing.T) {
|
|||||||
if tt.env.goos == "" {
|
if tt.env.goos == "" {
|
||||||
tt.env.goos = "linux"
|
tt.env.goos = "linux"
|
||||||
}
|
}
|
||||||
tt.env.flagSet = newUpFlagSet(tt.env.goos, &tt.env.upArgs)
|
tt.env.flagSet = newUpFlagSet(tt.env.goos, &tt.env.upArgs, "up")
|
||||||
flags := CleanUpArgs(tt.flags)
|
flags := CleanUpArgs(tt.flags)
|
||||||
if err := tt.env.flagSet.Parse(flags); err != nil {
|
if err := tt.env.flagSet.Parse(flags); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
32
cmd/tailscale/cli/login.go
Normal file
32
cmd/tailscale/cli/login.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
|
||||||
|
"github.com/peterbourgon/ff/v3/ffcli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var loginArgs upArgsT
|
||||||
|
|
||||||
|
var loginCmd = &ffcli.Command{
|
||||||
|
Name: "login",
|
||||||
|
ShortUsage: "[ALPHA] login [flags]",
|
||||||
|
ShortHelp: "Log in to a Tailscale account",
|
||||||
|
LongHelp: `"tailscale login" logs this machine in to your Tailscale network.
|
||||||
|
This command is currently in alpha and may change in the future.`,
|
||||||
|
UsageFunc: usageFunc,
|
||||||
|
FlagSet: func() *flag.FlagSet {
|
||||||
|
return newUpFlagSet(effectiveGOOS(), &loginArgs, "login")
|
||||||
|
}(),
|
||||||
|
Exec: func(ctx context.Context, args []string) error {
|
||||||
|
if err := localClient.SwitchToEmptyProfile(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return runUp(ctx, args, loginArgs)
|
||||||
|
},
|
||||||
|
}
|
122
cmd/tailscale/cli/switch.go
Normal file
122
cmd/tailscale/cli/switch.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/peterbourgon/ff/v3/ffcli"
|
||||||
|
"tailscale.com/ipn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var switchCmd = &ffcli.Command{
|
||||||
|
Name: "switch",
|
||||||
|
ShortHelp: "Switches to a different Tailscale profile",
|
||||||
|
FlagSet: func() *flag.FlagSet {
|
||||||
|
fs := flag.NewFlagSet("switch", flag.ExitOnError)
|
||||||
|
fs.BoolVar(&switchArgs.list, "list", false, "list available profiles")
|
||||||
|
return fs
|
||||||
|
}(),
|
||||||
|
Exec: switchProfile,
|
||||||
|
UsageFunc: func(*ffcli.Command) string {
|
||||||
|
return `USAGE
|
||||||
|
[ALPHA] switch <name>
|
||||||
|
[ALPHA] switch --list
|
||||||
|
|
||||||
|
"tailscale switch" switches between logged in profiles.
|
||||||
|
This command is currently in alpha and may change in the future.`
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var switchArgs struct {
|
||||||
|
list bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func listProfiles(ctx context.Context) error {
|
||||||
|
curP, all, err := localClient.ProfileStatus(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, prof := range all {
|
||||||
|
if prof.ID == curP.ID {
|
||||||
|
fmt.Printf("%s *\n", prof.Name)
|
||||||
|
} else {
|
||||||
|
fmt.Println(prof.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func switchProfile(ctx context.Context, args []string) error {
|
||||||
|
if switchArgs.list {
|
||||||
|
return listProfiles(ctx)
|
||||||
|
}
|
||||||
|
if len(args) != 1 {
|
||||||
|
outln("usage: tailscale profile switch NAME")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
cp, all, err := localClient.ProfileStatus(ctx)
|
||||||
|
if err != nil {
|
||||||
|
errf("Failed to switch to profile: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
var profID ipn.ProfileID
|
||||||
|
for _, p := range all {
|
||||||
|
if p.Name == args[0] {
|
||||||
|
profID = p.ID
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if profID == "" {
|
||||||
|
errf("No profile named %q\n", args[0])
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if profID == cp.ID {
|
||||||
|
printf("Already on profile %q\n", args[0])
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
if err := localClient.SwitchProfile(ctx, profID); err != nil {
|
||||||
|
errf("Failed to switch to profile: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
printf("Switching to profile %q\n", args[0])
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
errf("Timed out waiting for switch to complete.")
|
||||||
|
os.Exit(1)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
st, err := localClient.StatusWithoutPeers(ctx)
|
||||||
|
if err != nil {
|
||||||
|
errf("Error getting status: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
switch st.BackendState {
|
||||||
|
case "NoState", "Starting":
|
||||||
|
// TODO(maisem): maybe add a way to subscribe to state changes to
|
||||||
|
// LocalClient.
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
case "NeedsLogin":
|
||||||
|
outln("Logged out.")
|
||||||
|
outln("To log in, run:")
|
||||||
|
outln(" tailscale up")
|
||||||
|
return nil
|
||||||
|
case "Running":
|
||||||
|
outln("Success.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// For all other states, use the default error message.
|
||||||
|
if msg, ok := isRunningOrStarting(st); !ok {
|
||||||
|
outln(msg)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -58,7 +58,9 @@
|
|||||||
settings.)
|
settings.)
|
||||||
`),
|
`),
|
||||||
FlagSet: upFlagSet,
|
FlagSet: upFlagSet,
|
||||||
Exec: runUp,
|
Exec: func(ctx context.Context, args []string) error {
|
||||||
|
return runUp(ctx, args, upArgsGlobal)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func effectiveGOOS() string {
|
func effectiveGOOS() string {
|
||||||
@ -81,17 +83,19 @@ func acceptRouteDefault(goos string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var upFlagSet = newUpFlagSet(effectiveGOOS(), &upArgs)
|
var upFlagSet = newUpFlagSet(effectiveGOOS(), &upArgsGlobal, "up")
|
||||||
|
|
||||||
func inTest() bool { return flag.Lookup("test.v") != nil }
|
func inTest() bool { return flag.Lookup("test.v") != nil }
|
||||||
|
|
||||||
func newUpFlagSet(goos string, upArgs *upArgsT) *flag.FlagSet {
|
// newUpFlagSet returns a new flag set for the "up" and "login" commands.
|
||||||
upf := newFlagSet("up")
|
func newUpFlagSet(goos string, upArgs *upArgsT, cmd string) *flag.FlagSet {
|
||||||
|
if cmd != "up" && cmd != "login" {
|
||||||
|
panic("cmd must be up or login")
|
||||||
|
}
|
||||||
|
upf := newFlagSet(cmd)
|
||||||
|
|
||||||
upf.BoolVar(&upArgs.qr, "qr", false, "show QR code for login URLs")
|
upf.BoolVar(&upArgs.qr, "qr", false, "show QR code for login URLs")
|
||||||
upf.BoolVar(&upArgs.json, "json", false, "output in JSON format (WARNING: format subject to change)")
|
upf.StringVar(&upArgs.authKeyOrFile, "auth-key", "", `node authorization key; if it begins with "file:", then it's a path to a file containing the authkey`)
|
||||||
upf.BoolVar(&upArgs.forceReauth, "force-reauth", false, "force reauthentication")
|
|
||||||
upf.BoolVar(&upArgs.reset, "reset", false, "reset unspecified settings to their default values")
|
|
||||||
|
|
||||||
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")
|
||||||
@ -102,7 +106,6 @@ func newUpFlagSet(goos string, upArgs *upArgsT) *flag.FlagSet {
|
|||||||
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.BoolVar(&upArgs.runSSH, "ssh", false, "run an SSH server, permitting access per tailnet admin's declared policy")
|
upf.BoolVar(&upArgs.runSSH, "ssh", false, "run an SSH server, permitting access per tailnet admin's declared policy")
|
||||||
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\")")
|
||||||
upf.StringVar(&upArgs.authKeyOrFile, "auth-key", "", `node authorization key; if it begins with "file:", then it's a path to a file containing the authkey`)
|
|
||||||
upf.StringVar(&upArgs.hostname, "hostname", "", "hostname to use instead of the one provided by the OS")
|
upf.StringVar(&upArgs.hostname, "hostname", "", "hostname to use instead of the one provided by the OS")
|
||||||
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\") or empty string to not advertise routes")
|
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\") or empty string to not advertise routes")
|
||||||
upf.BoolVar(&upArgs.advertiseDefaultRoute, "advertise-exit-node", false, "offer to be an exit node for internet traffic for the tailnet")
|
upf.BoolVar(&upArgs.advertiseDefaultRoute, "advertise-exit-node", false, "offer to be an exit node for internet traffic for the tailnet")
|
||||||
@ -117,7 +120,14 @@ func newUpFlagSet(goos string, upArgs *upArgsT) *flag.FlagSet {
|
|||||||
upf.BoolVar(&upArgs.forceDaemon, "unattended", false, "run in \"Unattended Mode\" where Tailscale keeps running even after the current GUI user logs out (Windows-only)")
|
upf.BoolVar(&upArgs.forceDaemon, "unattended", false, "run in \"Unattended Mode\" where Tailscale keeps running even after the current GUI user logs out (Windows-only)")
|
||||||
}
|
}
|
||||||
upf.DurationVar(&upArgs.timeout, "timeout", 0, "maximum amount of time to wait for tailscaled to enter a Running state; default (0s) blocks forever")
|
upf.DurationVar(&upArgs.timeout, "timeout", 0, "maximum amount of time to wait for tailscaled to enter a Running state; default (0s) blocks forever")
|
||||||
registerAcceptRiskFlag(upf, &upArgs.acceptedRisks)
|
|
||||||
|
if cmd == "up" {
|
||||||
|
// Some flags are only for "up", not "login".
|
||||||
|
upf.BoolVar(&upArgs.json, "json", false, "output in JSON format (WARNING: format subject to change)")
|
||||||
|
upf.BoolVar(&upArgs.reset, "reset", false, "reset unspecified settings to their default values")
|
||||||
|
upf.BoolVar(&upArgs.forceReauth, "force-reauth", false, "force reauthentication")
|
||||||
|
registerAcceptRiskFlag(upf, &upArgs.acceptedRisks)
|
||||||
|
}
|
||||||
return upf
|
return upf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +177,7 @@ func (a upArgsT) getAuthKey() (string, error) {
|
|||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var upArgs upArgsT
|
var upArgsGlobal upArgsT
|
||||||
|
|
||||||
// Fields output when `tailscale up --json` is used. Two JSON blocks will be output.
|
// Fields output when `tailscale up --json` is used. Two JSON blocks will be output.
|
||||||
//
|
//
|
||||||
@ -427,7 +437,7 @@ func presentSSHToggleRisk(wantSSH, haveSSH bool, acceptedRisks string) error {
|
|||||||
return presentRiskToUser(riskLoseSSH, `You are connected using Tailscale SSH; this action will result in your session disconnecting.`, acceptedRisks)
|
return presentRiskToUser(riskLoseSSH, `You are connected using Tailscale SSH; this action will result in your session disconnecting.`, acceptedRisks)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runUp(ctx context.Context, args []string) (retErr error) {
|
func runUp(ctx context.Context, args []string, upArgs upArgsT) (retErr error) {
|
||||||
var egg bool
|
var egg bool
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
egg = fmt.Sprint(args) == "[up down down left right left right b a]"
|
egg = fmt.Sprint(args) == "[up down down left right left right b a]"
|
||||||
@ -936,7 +946,7 @@ func prefsToFlags(env upCheckEnv, prefs *ipn.Prefs) (flagVal map[string]any) {
|
|||||||
return env.curExitNodeIP.String()
|
return env.curExitNodeIP.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
fs := newUpFlagSet(env.goos, new(upArgsT) /* dummy */)
|
fs := newUpFlagSet(env.goos, new(upArgsT) /* dummy */, "up")
|
||||||
fs.VisitAll(func(f *flag.Flag) {
|
fs.VisitAll(func(f *flag.Flag) {
|
||||||
if preflessFlag(f.Name) {
|
if preflessFlag(f.Name) {
|
||||||
return
|
return
|
||||||
|
@ -125,7 +125,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
golang.org/x/crypto/pbkdf2 from software.sslmate.com/src/go-pkcs12
|
golang.org/x/crypto/pbkdf2 from software.sslmate.com/src/go-pkcs12
|
||||||
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
|
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
|
||||||
golang.org/x/exp/constraints from golang.org/x/exp/slices
|
golang.org/x/exp/constraints from golang.org/x/exp/slices
|
||||||
golang.org/x/exp/slices from tailscale.com/net/tsaddr
|
golang.org/x/exp/slices from tailscale.com/net/tsaddr+
|
||||||
golang.org/x/net/bpf from github.com/mdlayher/netlink+
|
golang.org/x/net/bpf from github.com/mdlayher/netlink+
|
||||||
golang.org/x/net/dns/dnsmessage from net+
|
golang.org/x/net/dns/dnsmessage from net+
|
||||||
golang.org/x/net/http/httpguts from net/http+
|
golang.org/x/net/http/httpguts from net/http+
|
||||||
|
Loading…
Reference in New Issue
Block a user