cmd/tailscale, logtail: add 'tailscale debug daemon-logs' logtail mechanism

Fixes #6836

Change-Id: Ia6eb39ff8972e1aa149aeeb63844a97497c2cf04
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2022-12-23 20:54:30 -08:00
committed by Brad Fitzpatrick
parent 5f96d6211a
commit b657187a69
4 changed files with 149 additions and 0 deletions

View File

@@ -34,6 +34,7 @@ import (
"tailscale.com/ipn"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/ipnstate"
"tailscale.com/logtail"
"tailscale.com/net/netutil"
"tailscale.com/safesocket"
"tailscale.com/tailcfg"
@@ -77,6 +78,7 @@ var handler = map[string]localAPIHandler{
"id-token": (*Handler).serveIDToken,
"login-interactive": (*Handler).serveLoginInteractive,
"logout": (*Handler).serveLogout,
"logtap": (*Handler).serveLogTap,
"metrics": (*Handler).serveMetrics,
"ping": (*Handler).servePing,
"prefs": (*Handler).servePrefs,
@@ -421,6 +423,45 @@ func (h *Handler) serveGoroutines(w http.ResponseWriter, r *http.Request) {
w.Write(buf)
}
// serveLogTap taps into the tailscaled/logtail server output and streams
// it to the client.
func (h *Handler) serveLogTap(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// Require write access (~root) as the logs could contain something
// sensitive.
if !h.PermitWrite {
http.Error(w, "logtap access denied", http.StatusForbidden)
return
}
if r.Method != "GET" {
http.Error(w, "GET required", http.StatusMethodNotAllowed)
return
}
f, ok := w.(http.Flusher)
if !ok {
http.Error(w, "streaming unsupported", http.StatusInternalServerError)
return
}
io.WriteString(w, `{"text":"[logtap connected]\n"}`+"\n")
f.Flush()
msgc := make(chan string, 16)
unreg := logtail.RegisterLogTap(msgc)
defer unreg()
for {
select {
case <-ctx.Done():
return
case msg := <-msgc:
io.WriteString(w, msg)
f.Flush()
}
}
}
func (h *Handler) serveMetrics(w http.ResponseWriter, r *http.Request) {
// Require write access out of paranoia that the metrics
// might contain something sensitive.