mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-16 03:31:39 +00:00
cmd/tailscale: add start of "debug derp" subcommand
Updates #6526 Change-Id: I84e440a8bd837c383000ce0cec4ff36b24249e8b Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
b0545873e5
commit
109aa3b2fb
@ -981,6 +981,15 @@ func (lc *LocalClient) DeleteProfile(ctx context.Context, profile ipn.ProfileID)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (lc *LocalClient) DebugDERPRegion(ctx context.Context, regionIDOrCode string) (*ipnstate.DebugDERPRegionReport, error) {
|
||||||
|
v := url.Values{"region": {regionIDOrCode}}
|
||||||
|
body, err := lc.send(ctx, "POST", "/localapi/v0/debug-derp-region?"+v.Encode(), 200, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error %w: %s", err, body)
|
||||||
|
}
|
||||||
|
return decodeJSON[*ipnstate.DebugDERPRegionReport](body)
|
||||||
|
}
|
||||||
|
|
||||||
// WatchIPNMask are filtering options for LocalClient.WatchIPNBus.
|
// WatchIPNMask are filtering options for LocalClient.WatchIPNBus.
|
||||||
//
|
//
|
||||||
// The zero value is a valid WatchOpt that means to watch everything.
|
// The zero value is a valid WatchOpt that means to watch everything.
|
||||||
|
@ -37,6 +37,7 @@ import (
|
|||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
|
"tailscale.com/util/must"
|
||||||
"tailscale.com/util/strs"
|
"tailscale.com/util/strs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -160,6 +161,11 @@ var debugCmd = &ffcli.Command{
|
|||||||
return fs
|
return fs
|
||||||
})(),
|
})(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "derp",
|
||||||
|
Exec: runDebugDERP,
|
||||||
|
ShortHelp: "test a DERP configuration",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -610,3 +616,15 @@ func runDevStoreSet(ctx context.Context, args []string) error {
|
|||||||
}
|
}
|
||||||
return localClient.SetDevStoreKeyValue(ctx, key, val)
|
return localClient.SetDevStoreKeyValue(ctx, key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runDebugDERP(ctx context.Context, args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return errors.New("usage: debug derp <region>")
|
||||||
|
}
|
||||||
|
st, err := localClient.DebugDERPRegion(ctx, args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("%s\n", must.Get(json.MarshalIndent(st, "", " ")))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -108,6 +108,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
tailscale.com/util/lineread from tailscale.com/net/interfaces+
|
tailscale.com/util/lineread from tailscale.com/net/interfaces+
|
||||||
tailscale.com/util/mak from tailscale.com/net/netcheck+
|
tailscale.com/util/mak from tailscale.com/net/netcheck+
|
||||||
tailscale.com/util/multierr from tailscale.com/control/controlhttp
|
tailscale.com/util/multierr from tailscale.com/control/controlhttp
|
||||||
|
tailscale.com/util/must from tailscale.com/cmd/tailscale/cli
|
||||||
tailscale.com/util/quarantine from tailscale.com/cmd/tailscale/cli
|
tailscale.com/util/quarantine from tailscale.com/cmd/tailscale/cli
|
||||||
tailscale.com/util/singleflight from tailscale.com/net/dnscache
|
tailscale.com/util/singleflight from tailscale.com/net/dnscache
|
||||||
tailscale.com/util/strs from tailscale.com/hostinfo+
|
tailscale.com/util/strs from tailscale.com/hostinfo+
|
||||||
|
@ -618,3 +618,11 @@ func sortKey(ps *PeerStatus) string {
|
|||||||
raw := ps.PublicKey.Raw32()
|
raw := ps.PublicKey.Raw32()
|
||||||
return string(raw[:])
|
return string(raw[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DebugDERPRegionReport is the result of a "tailscale debug derp" command,
|
||||||
|
// to let people debug a custom DERP setup.
|
||||||
|
type DebugDERPRegionReport struct {
|
||||||
|
Info []string
|
||||||
|
Warnings []string
|
||||||
|
Errors []string
|
||||||
|
}
|
||||||
|
92
ipn/localapi/debugderp.go
Normal file
92
ipn/localapi/debugderp.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// 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 localapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"tailscale.com/ipn/ipnstate"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) serveDebugDERPRegion(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !h.PermitWrite {
|
||||||
|
http.Error(w, "debug access denied", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.Method != "POST" {
|
||||||
|
http.Error(w, "POST required", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var st ipnstate.DebugDERPRegionReport
|
||||||
|
defer func() {
|
||||||
|
j, _ := json.Marshal(st)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write(j)
|
||||||
|
}()
|
||||||
|
|
||||||
|
dm := h.b.DERPMap()
|
||||||
|
if dm == nil {
|
||||||
|
st.Errors = append(st.Errors, "no DERP map (not connected?)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
regStr := r.FormValue("region")
|
||||||
|
var reg *tailcfg.DERPRegion
|
||||||
|
if id, err := strconv.Atoi(regStr); err == nil {
|
||||||
|
reg = dm.Regions[id]
|
||||||
|
} else {
|
||||||
|
for _, r := range dm.Regions {
|
||||||
|
if r.RegionCode == regStr {
|
||||||
|
reg = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if reg == nil {
|
||||||
|
st.Errors = append(st.Errors, fmt.Sprintf("no such region %q in DERP map", regStr))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st.Info = append(st.Info, fmt.Sprintf("Region %v == %q", reg.RegionID, reg.RegionCode))
|
||||||
|
|
||||||
|
if reg.Avoid {
|
||||||
|
st.Warnings = append(st.Warnings, "Region is marked with Avoid bit")
|
||||||
|
}
|
||||||
|
if len(reg.Nodes) == 0 {
|
||||||
|
st.Errors = append(st.Errors, "Region has no nodes defined")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bradfitz): finish:
|
||||||
|
// * first try TCP connection
|
||||||
|
// * reconnect 4 or 5 times; see if we ever get a different server key.
|
||||||
|
// if so, they're load balancing the wrong way. error.
|
||||||
|
// * try to DERP auth with new public key.
|
||||||
|
// * if rejected, add Info that it's likely the DERP server authz is on,
|
||||||
|
// try with LocalBackend's node key instead.
|
||||||
|
// * if they have more then one node, try to relay a packet between them
|
||||||
|
// and see if it works (like cmd/derpprobe). But if server authz is on,
|
||||||
|
// we won't be able to, so just warn. Say to turn that off, try again,
|
||||||
|
// then turn it back on. TODO(bradfitz): maybe add a debug frame to DERP
|
||||||
|
// protocol to say how many peers it's meshed with. Should match count
|
||||||
|
// in DERPRegion. Or maybe even list all their server pub keys that it's peered
|
||||||
|
// with.
|
||||||
|
// * try STUN queries
|
||||||
|
// * warn about IPv6 only
|
||||||
|
// * If their certificate is bad, either expired or just wrongly
|
||||||
|
// issued in the first place, tell them specifically that the
|
||||||
|
// cert is bad not just that the connection failed.
|
||||||
|
// * If /generate_204 on port 80 cannot be reached, warn
|
||||||
|
// that they won't get captive portal detection and
|
||||||
|
// should allow port 80.
|
||||||
|
// * If they have exactly one DERP region because they
|
||||||
|
// removed all of Tailscale's DERPs, warn that they have
|
||||||
|
// a SPOF that will hamper even direct connections from
|
||||||
|
// working. (warning, not error, as that's probably a likely
|
||||||
|
// config for headscale users)
|
||||||
|
st.Info = append(st.Info, "TODO: 🦉")
|
||||||
|
}
|
@ -65,6 +65,7 @@ var handler = map[string]localAPIHandler{
|
|||||||
"check-prefs": (*Handler).serveCheckPrefs,
|
"check-prefs": (*Handler).serveCheckPrefs,
|
||||||
"component-debug-logging": (*Handler).serveComponentDebugLogging,
|
"component-debug-logging": (*Handler).serveComponentDebugLogging,
|
||||||
"debug": (*Handler).serveDebug,
|
"debug": (*Handler).serveDebug,
|
||||||
|
"debug-derp-region": (*Handler).serveDebugDERPRegion,
|
||||||
"derpmap": (*Handler).serveDERPMap,
|
"derpmap": (*Handler).serveDERPMap,
|
||||||
"dev-set-state-store": (*Handler).serveDevSetStateStore,
|
"dev-set-state-store": (*Handler).serveDevSetStateStore,
|
||||||
"dial": (*Handler).serveDial,
|
"dial": (*Handler).serveDial,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user