ipn/localapi, cmd/tailscale: add API to get prefs, CLI debug command to show
Updates #1436 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>pull/1670/head
parent
03be116997
commit
50b309c1eb
|
@ -18,6 +18,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/paths"
|
"tailscale.com/paths"
|
||||||
"tailscale.com/safesocket"
|
"tailscale.com/safesocket"
|
||||||
|
@ -199,3 +200,15 @@ func CheckIPForwarding(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,8 @@ var debugCmd = &ffcli.Command{
|
||||||
fs := flag.NewFlagSet("debug", flag.ExitOnError)
|
fs := flag.NewFlagSet("debug", flag.ExitOnError)
|
||||||
fs.BoolVar(&debugArgs.goroutines, "daemon-goroutines", false, "If true, dump the tailscaled daemon's goroutines")
|
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.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.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.BoolVar(&debugArgs.localCreds, "local-creds", false, "print how to connect to local tailscaled")
|
||||||
fs.StringVar(&debugArgs.file, "file", "", "get, delete:NAME, or NAME")
|
fs.StringVar(&debugArgs.file, "file", "", "get, delete:NAME, or NAME")
|
||||||
|
@ -43,6 +45,8 @@ var debugArgs struct {
|
||||||
ipn bool
|
ipn bool
|
||||||
netMap bool
|
netMap bool
|
||||||
file string
|
file string
|
||||||
|
prefs bool
|
||||||
|
pretty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDebug(ctx context.Context, args []string) error {
|
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())
|
fmt.Printf("curl --unix-socket %s http://foo/localapi/v0/status\n", paths.DefaultTailscaledSocket())
|
||||||
return nil
|
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 {
|
if debugArgs.goroutines {
|
||||||
goroutines, err := tailscale.Goroutines(ctx)
|
goroutines, err := tailscale.Goroutines(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -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/derphttp from tailscale.com/net/netcheck
|
||||||
tailscale.com/derp/derpmap from tailscale.com/cmd/tailscale/cli
|
tailscale.com/derp/derpmap from tailscale.com/cmd/tailscale/cli
|
||||||
tailscale.com/disco from tailscale.com/derp
|
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/ipn/ipnstate from tailscale.com/cmd/tailscale/cli+
|
||||||
tailscale.com/metrics from tailscale.com/derp
|
tailscale.com/metrics from tailscale.com/derp
|
||||||
tailscale.com/net/dnscache from tailscale.com/derp/derphttp
|
tailscale.com/net/dnscache from tailscale.com/derp/derphttp
|
||||||
|
|
|
@ -238,6 +238,19 @@ func (b *LocalBackend) Shutdown() {
|
||||||
b.e.Wait()
|
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
|
// Status returns the latest status of the backend and its
|
||||||
// sub-components.
|
// sub-components.
|
||||||
func (b *LocalBackend) Status() *ipnstate.Status {
|
func (b *LocalBackend) Status() *ipnstate.Status {
|
||||||
|
|
|
@ -87,6 +87,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
h.serveGoroutines(w, r)
|
h.serveGoroutines(w, r)
|
||||||
case "/localapi/v0/status":
|
case "/localapi/v0/status":
|
||||||
h.serveStatus(w, r)
|
h.serveStatus(w, r)
|
||||||
|
case "/localapi/v0/prefs":
|
||||||
|
h.servePrefs(w, r)
|
||||||
case "/localapi/v0/check-ip-forwarding":
|
case "/localapi/v0/check-ip-forwarding":
|
||||||
h.serveCheckIPForwarding(w, r)
|
h.serveCheckIPForwarding(w, r)
|
||||||
case "/localapi/v0/bugreport":
|
case "/localapi/v0/bugreport":
|
||||||
|
@ -198,6 +200,17 @@ func (h *Handler) serveStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
e.Encode(st)
|
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) {
|
func (h *Handler) serveFiles(w http.ResponseWriter, r *http.Request) {
|
||||||
if !h.PermitWrite {
|
if !h.PermitWrite {
|
||||||
http.Error(w, "file access denied", http.StatusForbidden)
|
http.Error(w, "file access denied", http.StatusForbidden)
|
||||||
|
|
Loading…
Reference in New Issue