diff --git a/hscontrol/app.go b/hscontrol/app.go index 7a1ee95b..48f375fa 100644 --- a/hscontrol/app.go +++ b/hscontrol/app.go @@ -36,7 +36,6 @@ import ( "github.com/juanfont/headscale/hscontrol/util" zerolog "github.com/philip-bui/grpc-zerolog" "github.com/pkg/profile" - "github.com/prometheus/client_golang/prometheus/promhttp" zl "github.com/rs/zerolog" "github.com/rs/zerolog/log" "golang.org/x/crypto/acme" @@ -53,7 +52,6 @@ import ( "gorm.io/gorm" "tailscale.com/envknob" "tailscale.com/tailcfg" - "tailscale.com/tsweb" "tailscale.com/types/dnstype" "tailscale.com/types/key" "tailscale.com/util/dnsname" @@ -787,27 +785,12 @@ func (h *Headscale) Serve() error { log.Info(). Msgf("listening and serving HTTP on: %s", h.cfg.Addr) - debugMux := http.NewServeMux() - debug := tsweb.Debugger(debugMux) - debug.Handle("notifier", "Inspect notifier", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte(h.nodeNotifier.String())) - })) - debug.URL("/metrics", "Prometheus metrics") - debugMux.Handle("/metrics", promhttp.Handler()) - - debugHTTPServer := &http.Server{ - Addr: h.cfg.MetricsAddr, - Handler: debugMux, - ReadTimeout: types.HTTPTimeout, - WriteTimeout: 0, - } - debugHTTPListener, err := net.Listen("tcp", h.cfg.MetricsAddr) if err != nil { return fmt.Errorf("failed to bind to TCP address: %w", err) } + debugHTTPServer := h.debugHTTPServer() errorGroup.Go(func() error { return debugHTTPServer.Serve(debugHTTPListener) }) log.Info(). diff --git a/hscontrol/debug.go b/hscontrol/debug.go new file mode 100644 index 00000000..0dcd9dd1 --- /dev/null +++ b/hscontrol/debug.go @@ -0,0 +1,80 @@ +package hscontrol + +import ( + "encoding/json" + "net/http" + + "github.com/juanfont/headscale/hscontrol/types" + "github.com/prometheus/client_golang/prometheus/promhttp" + "tailscale.com/tsweb" +) + +func (h *Headscale) debugHTTPServer() *http.Server { + debugMux := http.NewServeMux() + debug := tsweb.Debugger(debugMux) + debug.Handle("notifier", "Connected nodes in notifier", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(h.nodeNotifier.String())) + })) + debug.Handle("config", "Current configuration", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + config, err := json.MarshalIndent(h.cfg, "", " ") + if err != nil { + httpError(w, err) + } + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + w.Write(config) + })) + debug.Handle("policy", "Current policy", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + pol, err := h.policyBytes() + if err != nil { + httpError(w, err) + } + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + w.Write(pol) + })) + debug.Handle("filter", "Current filter", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + filter := h.polMan.Filter() + + filterJSON, err := json.MarshalIndent(filter, "", " ") + if err != nil { + httpError(w, err) + } + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + w.Write(filterJSON) + })) + debug.Handle("derpmap", "Current DERPMap", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + dm := h.DERPMap + + dmJSON, err := json.MarshalIndent(dm, "", " ") + if err != nil { + httpError(w, err) + } + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + w.Write(dmJSON) + })) + debug.Handle("registration-cache", "Pending registrations", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + registrationsJSON, err := json.MarshalIndent(h.registrationCache.Items(), "", " ") + if err != nil { + httpError(w, err) + } + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + w.Write(registrationsJSON) + })) + + debug.URL("/metrics", "Prometheus metrics") + debugMux.Handle("/metrics", promhttp.Handler()) + + debugHTTPServer := &http.Server{ + Addr: h.cfg.MetricsAddr, + Handler: debugMux, + ReadTimeout: types.HTTPTimeout, + WriteTimeout: 0, + } + + return debugHTTPServer +}