FIXED:
* Better request tracking through flow

ADDED:
* Cross-check detection
This commit is contained in:
brent saner
2025-12-17 19:21:07 -05:00
parent 9f97fcaf81
commit 1a4549ea72
7 changed files with 73 additions and 40 deletions

View File

@@ -19,6 +19,7 @@ import (
sysd `github.com/coreos/go-systemd/daemon`
`github.com/davecgh/go-spew/spew`
`github.com/goccy/go-yaml`
`github.com/google/uuid`
`r00t2.io/clientinfo/version`
`r00t2.io/goutils/multierr`
)
@@ -261,6 +262,7 @@ func (s *Server) Reload() (err error) {
}
func (s *Server) explicit404(resp http.ResponseWriter, req *http.Request) {
s.log.Debug("server.Server.explicit404: '%s' requested '%s %s'", req.RemoteAddr, req.Method, req.URL.String())
resp.WriteHeader(http.StatusNotFound)
}
@@ -278,10 +280,12 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
var parsedFmts []*parsedMIME
var renderer outerRenderer
var includeFmt string
var reqId uuid.UUID = uuid.New()
var params url.Values = make(url.Values)
var outerFmt string = mediaJSON
s.log.Debug("server.Server.handleDefault: Handling request:\n%s", spew.Sdump(req))
s.log.Info("server.Server.handleDefault: '%s' (%s): '%s %s'", reqId.String(), req.RemoteAddr, req.Method, req.URL.String())
s.log.Debug("server.Server.handleDefault: Handling request for '%s':\n%s", reqId.String(), spew.Sdump(req))
origin = req.Header.Get("Origin")
if s.corsOrigins != nil && len(s.corsOrigins) != 0 && origin != "" {
if _, ok = s.corsOrigins[origin]; ok {
@@ -291,6 +295,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
}
resp.Header().Set("ClientInfo-Version", version.Ver.Short())
resp.Header().Set(httpCIDHdr, reqId.String())
page = &Page{
Info: &R00tInfo{
@@ -305,6 +310,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
RawFmt: nil,
Indent: "",
DoIndent: false,
ReqUUID: reqId,
}
// First the client info.
@@ -316,7 +322,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
}
if remAddrPort != "" {
if nAP, err = netip.ParseAddrPort(remAddrPort); err != nil {
s.log.Warning("server.Server.handleDefault: Failed to parse remote address '%s': %v", remAddrPort, err)
s.log.Warning("server.Server.handleDefault: Failed to parse remote address '%s' for '%s': %v", remAddrPort, reqId.String(), err)
// Don't return an error in case we're doing weird things like direct socket clients.
/*
http.Error(resp, "ERROR: Failed to parse client address", http.StatusInternalServerError)
@@ -329,13 +335,16 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
}
if req.URL != nil {
params = req.URL.Query()
if params.Has(urlParamXCheck) {
s.log.Info("server.Server.handleDefault: Cross-stack check: '%s' => '%s'", params.Get(urlParamXCheck), reqId.String())
}
}
uas = req.Header.Values("User-Agent")
if uas != nil && len(uas) > 0 {
page.Info.Client = make([]*R00tClient, 0, len(uas))
for _, ua := range uas {
if parsedUA, err = NewClient(ua); err != nil {
s.log.Err("server.Server.handleDefault: Failed to create client for '%s': %v", ua, err)
s.log.Err("server.Server.handleDefault: Failed to create client for UA '%s' via request '%s': %v", ua, reqId.String(), err)
http.Error(resp, fmt.Sprintf("ERROR: Failed to parse 'User-Agent' '%s'", ua), http.StatusInternalServerError)
return
}
@@ -363,7 +372,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
reqdMimes = req.Header.Values("Accept")
if reqdMimes != nil && len(reqdMimes) > 0 {
if parsedFmts, err = parseAccept(strings.Join(reqdMimes, ",")); err != nil {
s.log.Err("server.Server.handleDefault: Failed to parse Accept header '%#v' for '%s': %v", reqdMimes, remAddrPort, err)
s.log.Err("server.Server.handleDefault: Failed to parse Accept header '%#v' for '%s' ('%s'): %v", reqdMimes, remAddrPort, reqId.String(), err)
resp.Header()["Accept"] = okAcceptMime
http.Error(
resp,
@@ -374,12 +383,12 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
}
if outerFmt, err = decideParseAccept(parsedFmts, outerFmt); err != nil {
if errors.Is(err, ErrUnsupportedMIME) {
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' via '%#v'.", remAddrPort, reqdMimes)
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' via '%#v' for '%s'.", remAddrPort, reqdMimes, reqId.String())
resp.Header()["Accept"] = okAcceptMime
http.Error(resp, "ERROR: No supported MIME type specified via request 'Accept'; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
return
} else {
s.log.Err("server.Server.handleDefault: Received unknown error choosing from Accept header for '%s': %v", remAddrPort, err)
s.log.Err("server.Server.handleDefault: Received unknown error choosing from Accept header for '%s' from '%s': %v", remAddrPort, reqId.String(), err)
http.Error(resp, "ERROR: Unknown error occurred when negotiating MIME type.", http.StatusInternalServerError)
return
}
@@ -388,7 +397,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
// `mime` URL query parameter.
if params.Has("mime") {
if parsedFmts, err = parseAccept(strings.Join(params["mime"], ",")); err != nil {
s.log.Err("server.Server.handleDefault: Failed to parse 'mime' URL parameter '%#v' for '%s': %v", params["mime"], remAddrPort, err)
s.log.Err("server.Server.handleDefault: Failed to parse 'mime' URL parameter '%#v' for '%s' (%s): %v", params["mime"], remAddrPort, reqId.String(), err)
resp.Header()["Accept"] = okAcceptMime
http.Error(
resp,
@@ -399,12 +408,12 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
}
if outerFmt, err = decideParseAccept(parsedFmts, outerFmt); err != nil {
if errors.Is(err, ErrUnsupportedMIME) {
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' via '%#v'.", remAddrPort, params["mime"])
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' (%s) via '%#v'.", remAddrPort, reqId.String(), params["mime"])
resp.Header()["Accept"] = okAcceptMime
http.Error(resp, "ERROR: No supported MIME type specified via URL parameter 'mime'; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
return
} else {
s.log.Err("server.Server.handleDefault: Received unknown error choosing from 'mime' URL parameter for '%s': %v", remAddrPort, err)
s.log.Err("server.Server.handleDefault: Received unknown error choosing from 'mime' URL parameter for '%s' (%s): %v", remAddrPort, reqId.String(), err)
http.Error(resp, "ERROR: Unknown error occurred when negotiating MIME type.", http.StatusInternalServerError)
return
}
@@ -413,7 +422,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
// 'include' URL query parameter (only for text/html).
if outerFmt == mediaHTML && params.Has("include") {
if parsedFmts, err = parseAccept(strings.Join(params["include"], ",")); err != nil {
s.log.Err("server.Server.handleDefault: Failed to parse 'include' URL parameter '%#v' for '%s': %v", params["include"], remAddrPort, err)
s.log.Err("server.Server.handleDefault: Failed to parse 'include' URL parameter '%#v' for '%s' (%s): %v", params["include"], remAddrPort, reqId.String(), err)
resp.Header()["Accept"] = okAcceptMime
http.Error(
resp,
@@ -424,12 +433,12 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
}
if includeFmt, err = decideParseAccept(parsedFmts, includeFmt); err != nil {
if errors.Is(err, ErrUnsupportedMIME) {
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' via '%#v'.", remAddrPort, params["include"])
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' (%s) via '%#v'.", remAddrPort, reqId.String(), params["include"])
resp.Header()["Accept"] = okAcceptMime
http.Error(resp, "ERROR: No supported MIME type specified via URL parameter 'include'; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
return
} else {
s.log.Err("server.Server.handleDefault: Received unknown error choosing from 'include' URL parameter for '%s': %v", remAddrPort, err)
s.log.Err("server.Server.handleDefault: Received unknown error choosing from 'include' URL parameter for '%s' (%s): %v", remAddrPort, reqId.String(), err)
http.Error(resp, "ERROR: Unknown error occurred when negotiating MIME type.", http.StatusInternalServerError)
return
}
@@ -459,13 +468,13 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
case mediaYAML:
renderer = s.renderYML
default:
s.log.Err("server.Server.handleDefault: Unknown output format '%s'", outerFmt)
s.log.Err("server.Server.handleDefault: Unknown output format '%s' from '%s' (%s)", outerFmt, remAddrPort, reqId.String())
http.Error(resp, "ERROR: Unable to determine default renderer.", http.StatusInternalServerError)
return
}
if err = renderer(page, resp); err != nil {
s.log.Err("server.Server.handleDefault: Failed to render request from '%s' as '%s': %v", remAddrPort, outerFmt, err)
s.log.Err("server.Server.handleDefault: Failed to render request from '%s' (%s) as '%s': %v", remAddrPort, reqId.String(), outerFmt, err)
// The renderer handles the error-handling with the client.
return
}
@@ -481,20 +490,23 @@ func (s *Server) handleAbout(resp http.ResponseWriter, req *http.Request) {
Req: req,
},
PageType: "about",
ReqUUID: uuid.New(),
}
s.log.Debug("server.Server.handleAbout: Handling request:\n%s", spew.Sdump(req))
s.log.Info("server.Server.handleAbout: '%s' (%s)", renderPage.ReqUUID.String(), req.RemoteAddr)
s.log.Debug("server.Server.handleAbout: Handling request for '%s':\n%s", renderPage.ReqUUID.String(), spew.Sdump(req))
resp.Header().Set("ClientInfo-Version", version.Ver.Short())
resp.Header().Set("Content-Type", "text/html; charset=utf-8")
resp.Header().Set(httpCIDHdr, renderPage.ReqUUID.String())
if err = tpl.ExecuteTemplate(resp, "about", renderPage); err != nil {
s.log.Err("server.Server.handleAbout: Failed to execute template for '%s': %v", req.RemoteAddr, err)
s.log.Err("server.Server.handleAbout: Failed to execute template for '%s': %v", renderPage.ReqUUID.String(), err)
http.Error(resp, "ERROR: Failed to render HTML", http.StatusInternalServerError)
return
}
s.log.Debug("server.Server.handleAbout: Handled request:\n%s", spew.Sdump(req))
s.log.Debug("server.Server.handleAbout: Handled request for '%s'", renderPage.ReqUUID.String())
return
}
@@ -507,20 +519,23 @@ func (s *Server) handleUsage(resp http.ResponseWriter, req *http.Request) {
Req: req,
},
PageType: "usage",
ReqUUID: uuid.New(),
}
s.log.Debug("server.Server.handleUsage: Handling request:\n%s", spew.Sdump(req))
s.log.Info("server.Server.handleUsage: '%s' (%s)", renderPage.ReqUUID.String(), req.RemoteAddr)
s.log.Debug("server.Server.handleUsage: Handling request for '%s':\n%s", renderPage.ReqUUID.String(), spew.Sdump(req))
resp.Header().Set("ClientInfo-Version", version.Ver.Short())
resp.Header().Set("Content-Type", "text/html; charset=utf-8")
resp.Header().Set(httpCIDHdr, renderPage.ReqUUID.String())
if err = tpl.ExecuteTemplate(resp, "usage", renderPage); err != nil {
s.log.Err("server.Server.handleAbout: Failed to execute template for '%s': %v", req.RemoteAddr, err)
s.log.Err("server.Server.handleAbout: Failed to execute template for '%s' (%s): %v", renderPage.ReqUUID.String(), req.RemoteAddr, err)
http.Error(resp, "ERROR: Failed to render HTML", http.StatusInternalServerError)
return
}
s.log.Debug("server.Server.handleUsage: Handled request:\n%s", spew.Sdump(req))
s.log.Debug("server.Server.handleUsage: Handled request for '%s'", renderPage.ReqUUID.String())
return
}
@@ -533,7 +548,7 @@ func (s *Server) renderJSON(page *Page, resp http.ResponseWriter) (err error) {
if page.DoIndent {
if b, err = json.MarshalIndent(page.Info, "", page.Indent); err != nil {
s.log.Err("server.Server.renderJSON: Failed to render to indented JSON: %v", err)
s.log.Err("server.Server.renderJSON: Failed to render to indented JSON for '%s': %v", page.ReqUUID.String(), err)
http.Error(resp, "ERROR: Failed to render JSON", http.StatusInternalServerError)
return
}
@@ -566,13 +581,13 @@ func (s *Server) renderHTML(page *Page, resp http.ResponseWriter) (err error) {
case mediaJSON, mediaXML:
if page.DoIndent {
if b, err = mediaIndent[*page.RawFmt](page.Info, "", page.Indent); err != nil {
s.log.Err("server.Server.renderHTML: Failed to render to indented include '%s': %v", *page.RawFmt, err)
s.log.Err("server.Server.renderHTML: Failed to render to indented include '%s' for '%s': %v", *page.RawFmt, page.ReqUUID.String(), err)
http.Error(resp, "ERROR: Failed to render include format", http.StatusInternalServerError)
return
}
} else {
if b, err = mediaNoIndent[*page.RawFmt](page.Info); err != nil {
s.log.Err("server.Server.renderHTML: Failed to render to include '%s': %v", *page.RawFmt, err)
s.log.Err("server.Server.renderHTML: Failed to render to include '%s' for '%s': %v", *page.RawFmt, page.ReqUUID.String(), err)
http.Error(resp, "ERROR: Failed to render include format", http.StatusInternalServerError)
return
}
@@ -580,7 +595,9 @@ func (s *Server) renderHTML(page *Page, resp http.ResponseWriter) (err error) {
// Non-indentable
case mediaYAML:
if b, err = mediaNoIndent[*page.RawFmt](page.Info); err != nil {
s.log.Err("server.Server.renderHTML: Failed to render to '%s': %v", *page.RawFmt, err)
s.log.Err("server.Server.renderHTML: Failed to render to '%s' for '%s': %v", *page.RawFmt, page.ReqUUID.String(), err)
http.Error(resp, "ERROR: Failed to render include format", http.StatusInternalServerError)
return
}
}
if b != nil {
@@ -592,7 +609,7 @@ func (s *Server) renderHTML(page *Page, resp http.ResponseWriter) (err error) {
resp.Header().Set("Content-Type", "text/html; charset=utf-8")
if err = tpl.ExecuteTemplate(resp, "index", page); err != nil {
s.log.Err("server.Server.renderHTML: Failed to render template: %v", err)
s.log.Err("server.Server.renderHTML: Failed to render template for '%s': %v", page.ReqUUID.String(), err)
http.Error(resp, "ERROR: Failed to render HTML", http.StatusInternalServerError)
return
}
@@ -606,13 +623,13 @@ func (s *Server) renderXML(page *Page, resp http.ResponseWriter) (err error) {
if page.DoIndent {
if b, err = xml.MarshalIndent(page.Info, "", page.Indent); err != nil {
s.log.Err("server.Server.renderXML: Failed to render to indented XML: %v", err)
s.log.Err("server.Server.renderXML: Failed to render to indented XML for '%s': %v", page.ReqUUID.String(), err)
http.Error(resp, "ERROR: Failed to render XML", http.StatusInternalServerError)
return
}
} else {
if b, err = xml.Marshal(page.Info); err != nil {
s.log.Err("server.Server.renderXML: Failed to render to XML: %v", err)
s.log.Err("server.Server.renderXML: Failed to render to XML for '%s': %v", page.ReqUUID.String(), err)
http.Error(resp, "ERROR: Failed to render XML", http.StatusInternalServerError)
return
}
@@ -621,7 +638,7 @@ func (s *Server) renderXML(page *Page, resp http.ResponseWriter) (err error) {
resp.Header().Set("Content-Type", "application/xml; charset=utf-8")
if _, err = resp.Write(b); err != nil {
s.log.Err("server.Server.renderXML: Failed to send XML: %v", err)
s.log.Err("server.Server.renderXML: Failed to send XML to '%s': %v", page.ReqUUID.String(), err)
return
}
@@ -633,15 +650,15 @@ func (s *Server) renderYML(page *Page, resp http.ResponseWriter) (err error) {
var b []byte
if b, err = yaml.Marshal(page.Info); err != nil {
s.log.Err("server.Server.renderJSON: Failed to render to JSON: %v", err)
http.Error(resp, "ERROR: Failed to render JSON", http.StatusInternalServerError)
s.log.Err("server.Server.renderYML: Failed to render to YAML for '%s': %v", page.ReqUUID.String(), err)
http.Error(resp, "ERROR: Failed to render YAML", http.StatusInternalServerError)
return
}
resp.Header().Set("Content-Type", "application/yaml")
if _, err = resp.Write(b); err != nil {
s.log.Err("server.Server.renderJSON: Failed to send JSON: %v", err)
s.log.Err("server.Server.renderYML: Failed to send YAML to '%s': %v", page.ReqUUID.String(), err)
return
}