mirror of https://github.com/coder/coder.git
chore: add tailscale magicsock debug logging controls (#8982)
This commit is contained in:
parent
a5c59b9934
commit
3c52b01850
|
@ -22,6 +22,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/armon/circbuf"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/spf13/afero"
|
||||
|
@ -1408,24 +1409,57 @@ func (a *agent) isClosed() bool {
|
|||
}
|
||||
|
||||
func (a *agent) HTTPDebug() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r := chi.NewRouter()
|
||||
|
||||
requireNetwork := func(w http.ResponseWriter) (*tailnet.Conn, bool) {
|
||||
a.closeMutex.Lock()
|
||||
network := a.network
|
||||
a.closeMutex.Unlock()
|
||||
|
||||
if network == nil {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, _ = w.Write([]byte("network is not ready yet"))
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return network, true
|
||||
}
|
||||
|
||||
r.Get("/debug/magicsock", func(w http.ResponseWriter, r *http.Request) {
|
||||
network, ok := requireNetwork(w)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
network.MagicsockServeHTTPDebug(w, r)
|
||||
})
|
||||
|
||||
r.Get("/debug/magicsock/debug-logging/{state}", func(w http.ResponseWriter, r *http.Request) {
|
||||
state := chi.URLParam(r, "state")
|
||||
stateBool, err := strconv.ParseBool(state)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = fmt.Fprintf(w, "invalid state %q, must be a boolean", state)
|
||||
return
|
||||
}
|
||||
|
||||
if r.URL.Path == "/debug/magicsock" {
|
||||
network.MagicsockServeHTTPDebug(w, r)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, _ = w.Write([]byte("404 not found"))
|
||||
network, ok := requireNetwork(w)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
network.MagicsockSetDebugLoggingEnabled(stateBool)
|
||||
a.logger.Info(r.Context(), "updated magicsock debug logging due to debug request", slog.F("new_state", stateBool))
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = fmt.Fprintf(w, "updated magicsock debug logging to %v", stateBool)
|
||||
})
|
||||
|
||||
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, _ = w.Write([]byte("404 not found"))
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (a *agent) Close() error {
|
||||
|
|
|
@ -1932,6 +1932,96 @@ func TestAgent_WriteVSCodeConfigs(t *testing.T) {
|
|||
}, testutil.WaitShort, testutil.IntervalFast)
|
||||
}
|
||||
|
||||
func TestAgent_DebugServer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
derpMap, _ := tailnettest.RunDERPAndSTUN(t)
|
||||
//nolint:dogsled
|
||||
conn, _, _, _, agnt := setupAgent(t, agentsdk.Manifest{
|
||||
DERPMap: derpMap,
|
||||
}, 0)
|
||||
|
||||
awaitReachableCtx := testutil.Context(t, testutil.WaitLong)
|
||||
ok := conn.AwaitReachable(awaitReachableCtx)
|
||||
require.True(t, ok)
|
||||
_ = conn.Close()
|
||||
|
||||
srv := httptest.NewServer(agnt.HTTPDebug())
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
t.Run("MagicsockDebug", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, srv.URL+"/debug/magicsock", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := srv.Client().Do(req)
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||
|
||||
resBody, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(resBody), "<h1>magicsock</h1>")
|
||||
})
|
||||
|
||||
t.Run("MagicsockDebugLogging", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Enable", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, srv.URL+"/debug/magicsock/debug-logging/t", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := srv.Client().Do(req)
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||
|
||||
resBody, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(resBody), "updated magicsock debug logging to true")
|
||||
})
|
||||
|
||||
t.Run("Disable", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, srv.URL+"/debug/magicsock/debug-logging/0", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := srv.Client().Do(req)
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||
|
||||
resBody, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(resBody), "updated magicsock debug logging to false")
|
||||
})
|
||||
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, srv.URL+"/debug/magicsock/debug-logging/blah", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := srv.Client().Do(req)
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
require.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||
|
||||
resBody, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(resBody), `invalid state "blah", must be a boolean`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func setupSSHCommand(t *testing.T, beforeArgs []string, afterArgs []string) (*ptytest.PTYCmd, pty.Process) {
|
||||
//nolint:dogsled
|
||||
agentConn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
|
@ -2013,7 +2103,7 @@ func setupAgent(t *testing.T, metadata agentsdk.Manifest, ptyTimeout time.Durati
|
|||
*agenttest.Client,
|
||||
<-chan *agentsdk.Stats,
|
||||
afero.Fs,
|
||||
io.Closer,
|
||||
agent.Agent,
|
||||
) {
|
||||
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||
if metadata.DERPMap == nil {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
@ -51,6 +52,14 @@ const (
|
|||
WorkspaceAgentSpeedtestPort = 3
|
||||
)
|
||||
|
||||
// EnvMagicsockDebugLogging enables super-verbose logging for the magicsock
|
||||
// internals. A logger must be supplied to the connection with the debug level
|
||||
// enabled.
|
||||
//
|
||||
// With this disabled, you still get a lot of output if you have a valid logger
|
||||
// with the debug level enabled.
|
||||
const EnvMagicsockDebugLogging = "CODER_MAGICSOCK_DEBUG_LOGGING"
|
||||
|
||||
func init() {
|
||||
// Globally disable network namespacing. All networking happens in
|
||||
// userspace.
|
||||
|
@ -175,6 +184,18 @@ func NewConn(options *Options) (conn *Conn, err error) {
|
|||
magicConn.SetDERPHeader(options.DERPHeader.Clone())
|
||||
}
|
||||
|
||||
if v, ok := os.LookupEnv(EnvMagicsockDebugLogging); ok {
|
||||
vBool, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
options.Logger.Debug(context.Background(), fmt.Sprintf("magicsock debug logging disabled due to invalid value %s=%q, use true or false", EnvMagicsockDebugLogging, v))
|
||||
} else {
|
||||
magicConn.SetDebugLoggingEnabled(vBool)
|
||||
options.Logger.Debug(context.Background(), fmt.Sprintf("magicsock debug logging set by %s=%t", EnvMagicsockDebugLogging, vBool))
|
||||
}
|
||||
} else {
|
||||
options.Logger.Debug(context.Background(), fmt.Sprintf("magicsock debug logging disabled, use %s=true to enable", EnvMagicsockDebugLogging))
|
||||
}
|
||||
|
||||
// Update the keys for the magic connection!
|
||||
err = magicConn.SetPrivateKey(nodePrivateKey)
|
||||
if err != nil {
|
||||
|
@ -361,6 +382,10 @@ type Conn struct {
|
|||
trafficStats *connstats.Statistics
|
||||
}
|
||||
|
||||
func (c *Conn) MagicsockSetDebugLoggingEnabled(enabled bool) {
|
||||
c.magicConn.SetDebugLoggingEnabled(enabled)
|
||||
}
|
||||
|
||||
func (c *Conn) SetAddresses(ips []netip.Prefix) error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
|
Loading…
Reference in New Issue