From 50b309c1ebc73b8e3470633cb94b3803b5805c63 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 7 Apr 2021 08:27:35 -0700 Subject: [PATCH] ipn/localapi, cmd/tailscale: add API to get prefs, CLI debug command to show Updates #1436 Signed-off-by: Brad Fitzpatrick --- client/tailscale/tailscale.go | 13 +++++++++++++ cmd/tailscale/cli/debug.go | 17 +++++++++++++++++ cmd/tailscale/depaware.txt | 2 +- ipn/ipnlocal/local.go | 13 +++++++++++++ ipn/localapi/localapi.go | 13 +++++++++++++ 5 files changed, 57 insertions(+), 1 deletion(-) diff --git a/client/tailscale/tailscale.go b/client/tailscale/tailscale.go index cb9c329ad..2d1734a80 100644 --- a/client/tailscale/tailscale.go +++ b/client/tailscale/tailscale.go @@ -18,6 +18,7 @@ "strconv" "strings" + "tailscale.com/ipn" "tailscale.com/ipn/ipnstate" "tailscale.com/paths" "tailscale.com/safesocket" @@ -199,3 +200,15 @@ func CheckIPForwarding(ctx context.Context) error { } return nil } + +func GetPrefs(ctx context.Context) (*ipn.Prefs, error) { + body, err := get200(ctx, "/localapi/v0/prefs") + if err != nil { + return nil, err + } + var p ipn.Prefs + if err := json.Unmarshal(body, &p); err != nil { + return nil, fmt.Errorf("invalid JSON from check-ip-forwarding: %w", err) + } + return &p, nil +} diff --git a/cmd/tailscale/cli/debug.go b/cmd/tailscale/cli/debug.go index c96724a09..851bb97de 100644 --- a/cmd/tailscale/cli/debug.go +++ b/cmd/tailscale/cli/debug.go @@ -30,6 +30,8 @@ fs := flag.NewFlagSet("debug", flag.ExitOnError) fs.BoolVar(&debugArgs.goroutines, "daemon-goroutines", false, "If true, dump the tailscaled daemon's goroutines") fs.BoolVar(&debugArgs.ipn, "ipn", false, "If true, subscribe to IPN notifications") + fs.BoolVar(&debugArgs.prefs, "prefs", false, "If true, dump active prefs") + fs.BoolVar(&debugArgs.pretty, "pretty", false, "If true, pretty-print output (for --prefs)") fs.BoolVar(&debugArgs.netMap, "netmap", true, "whether to include netmap in --ipn mode") fs.BoolVar(&debugArgs.localCreds, "local-creds", false, "print how to connect to local tailscaled") fs.StringVar(&debugArgs.file, "file", "", "get, delete:NAME, or NAME") @@ -43,6 +45,8 @@ ipn bool netMap bool file string + prefs bool + pretty bool } func runDebug(ctx context.Context, args []string) error { @@ -62,6 +66,19 @@ func runDebug(ctx context.Context, args []string) error { fmt.Printf("curl --unix-socket %s http://foo/localapi/v0/status\n", paths.DefaultTailscaledSocket()) return nil } + if debugArgs.prefs { + prefs, err := tailscale.GetPrefs(ctx) + if err != nil { + return err + } + if debugArgs.pretty { + fmt.Println(prefs.Pretty()) + } else { + j, _ := json.MarshalIndent(prefs, "", "\t") + fmt.Println(string(j)) + } + return nil + } if debugArgs.goroutines { goroutines, err := tailscale.Goroutines(ctx) if err != nil { diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index 52bd2fefc..9ae3d95a3 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -19,7 +19,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep tailscale.com/derp/derphttp from tailscale.com/net/netcheck tailscale.com/derp/derpmap from tailscale.com/cmd/tailscale/cli tailscale.com/disco from tailscale.com/derp - tailscale.com/ipn from tailscale.com/cmd/tailscale/cli + tailscale.com/ipn from tailscale.com/cmd/tailscale/cli+ tailscale.com/ipn/ipnstate from tailscale.com/cmd/tailscale/cli+ tailscale.com/metrics from tailscale.com/derp tailscale.com/net/dnscache from tailscale.com/derp/derphttp diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 4ce9e2345..f05865b68 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -238,6 +238,19 @@ func (b *LocalBackend) Shutdown() { b.e.Wait() } +// Prefs returns a copy of b's current prefs, with any private keys removed. +func (b *LocalBackend) Prefs() *ipn.Prefs { + b.mu.Lock() + defer b.mu.Unlock() + p := b.prefs.Clone() + if p != nil && p.Persist != nil { + p.Persist.LegacyFrontendPrivateMachineKey = wgkey.Private{} + p.Persist.PrivateNodeKey = wgkey.Private{} + p.Persist.OldPrivateNodeKey = wgkey.Private{} + } + return p +} + // Status returns the latest status of the backend and its // sub-components. func (b *LocalBackend) Status() *ipnstate.Status { diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index 2478ee8b5..178690e63 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -87,6 +87,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.serveGoroutines(w, r) case "/localapi/v0/status": h.serveStatus(w, r) + case "/localapi/v0/prefs": + h.servePrefs(w, r) case "/localapi/v0/check-ip-forwarding": h.serveCheckIPForwarding(w, r) case "/localapi/v0/bugreport": @@ -198,6 +200,17 @@ func (h *Handler) serveStatus(w http.ResponseWriter, r *http.Request) { e.Encode(st) } +func (h *Handler) servePrefs(w http.ResponseWriter, r *http.Request) { + if !h.PermitRead { + http.Error(w, "prefs access denied", http.StatusForbidden) + return + } + w.Header().Set("Content-Type", "application/json") + e := json.NewEncoder(w) + e.SetIndent("", "\t") + e.Encode(h.b.Prefs()) +} + func (h *Handler) serveFiles(w http.ResponseWriter, r *http.Request) { if !h.PermitWrite { http.Error(w, "file access denied", http.StatusForbidden)