1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2026-10-28 15:02:28 +00:00

monitoring: Use method-based routing and CSRF for http requests

The monitoring http server is mostly read-only but shows sensitive data,
and also has one mutating endpoint, /exit.

This patch updates the routing to use method-based routing, and also
wraps it on CSRF to reject cross-origin requests from browsers.

Note that does not affect direct POST /exit requests via tools like
curl or wget, which continue to work just like today. This is exercised
in the (unchanged) integration tests.
This commit is contained in:
Alberto Bertogli
2026-10-04 18:58:03 +01:00
parent 05e5803066
commit b23047aa83
3 changed files with 85 additions and 21 deletions
+29 -20
View File
@@ -91,6 +91,24 @@ func parseVersionInfo() {
func launchMonitoringServer(conf *config.Config) {
log.Infof("Monitoring HTTP server listening on %s", conf.MonitoringAddress)
srv := &http.Server{
Addr: conf.MonitoringAddress,
ReadHeaderTimeout: 10 * time.Second,
}
// Use the default mux, as that's where the expvar and pprof handlers
// register themselves.
srv.Handler = monitoringHandler(http.DefaultServeMux, srv, conf)
if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("Monitoring server failed: %v", err)
}
}
// monitoringHandler registers the monitoring handlers in the given mux, and
// returns the handler to use for the monitoring server.
func monitoringHandler(mux *http.ServeMux, srv *http.Server,
conf *config.Config) http.Handler {
osHostname, _ := os.Hostname()
indexData := struct {
@@ -109,27 +127,23 @@ func launchMonitoringServer(conf *config.Config) {
Hostname: osHostname,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
if err := monitoringHTMLIndex.Execute(w, indexData); err != nil {
log.Infof("monitoring handler error: %v", err)
}
})
srv := &http.Server{Addr: conf.MonitoringAddress}
mux.HandleFunc("POST /exit", exitHandler(srv))
mux.HandleFunc("GET /metrics", expvarom.MetricsHandler)
mux.HandleFunc("GET /debug/flags", debugFlagsHandler)
mux.HandleFunc("GET /debug/config", debugConfigHandler(conf))
mux.HandleFunc("GET /debug/traces", nettrace.RenderTraces)
http.HandleFunc("/exit", exitHandler(srv))
http.HandleFunc("/metrics", expvarom.MetricsHandler)
http.HandleFunc("/debug/flags", debugFlagsHandler)
http.HandleFunc("/debug/config", debugConfigHandler(conf))
http.HandleFunc("/debug/traces", nettrace.RenderTraces)
if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("Monitoring server failed: %v", err)
}
// Reject cross-origin requests from browsers, except for safe methods
// (like GET). This prevents cross-site request forgery: otherwise, any
// web page could make the browser of someone who can reach the monitoring
// server send it a POST /exit.
return http.NewCrossOriginProtection().Handler(mux)
}
// Functions available inside the templates.
@@ -207,11 +221,6 @@ os hostname <i>{{.Hostname}}</i><br>
func exitHandler(srv *http.Server) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Use POST method for exiting", http.StatusMethodNotAllowed)
return
}
log.Infof("Received /exit")
http.Error(w, "OK exiting", http.StatusOK)