Skip to content

Commit 9e532e7

Browse files
committed
fix: move pprof endpoints to telemetry server and protect with auth filter
1 parent 33b5ed4 commit 9e532e7

2 files changed

Lines changed: 63 additions & 7 deletions

File tree

pkg/app/server.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,14 @@ func buildTelemetryServer(registry prometheus.Gatherer, authFilter bool, kubeCon
466466
// Add metricsPath
467467
metricsHandler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorLog: sLogger})
468468

469+
pprofHandlers := map[string]http.Handler{
470+
"/debug/pprof/": http.HandlerFunc(pprof.Index),
471+
"/debug/pprof/cmdline": http.HandlerFunc(pprof.Cmdline),
472+
"/debug/pprof/profile": http.HandlerFunc(pprof.Profile),
473+
"/debug/pprof/symbol": http.HandlerFunc(pprof.Symbol),
474+
"/debug/pprof/trace": http.HandlerFunc(pprof.Trace),
475+
}
476+
469477
// Add Authentication/Authorization via Kubernetes API
470478
if authFilter {
471479
client, err := rest.HTTPClientFor(kubeConfig)
@@ -482,9 +490,23 @@ func buildTelemetryServer(registry prometheus.Gatherer, authFilter bool, kubeCon
482490
if err != nil {
483491
klog.ErrorS(err, "failed to apply metrics filter")
484492
}
493+
494+
for path, h := range pprofHandlers {
495+
protected, err := metricsFilter(klog.Background(), h)
496+
if err != nil {
497+
klog.ErrorS(err, "failed to apply auth filter to pprof handler", "path", path)
498+
delete(pprofHandlers, path)
499+
continue
500+
}
501+
pprofHandlers[path] = protected
502+
}
485503
}
486504
mux.Handle(metricsPath, metricsHandler)
487505

506+
for path, h := range pprofHandlers {
507+
mux.Handle(path, h)
508+
}
509+
488510
// Add readyzPath
489511
mux.Handle(readyzPath, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
490512
count, err := util.GatherAndCount(registry)
@@ -537,13 +559,6 @@ func handleClusterDelegationForProber(client kubernetes.Interface, probeType str
537559
func buildMetricsServer(m *metricshandler.MetricsHandler, durationObserver prometheus.ObserverVec, client kubernetes.Interface, authFilter bool, kubeConfig *rest.Config) *http.ServeMux {
538560
mux := http.NewServeMux()
539561

540-
// TODO: This doesn't belong into serveMetrics
541-
mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
542-
mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
543-
mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
544-
mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
545-
mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
546-
547562
// Add metricsPath
548563
metricsHandler := promhttp.InstrumentHandlerDuration(durationObserver, m)
549564

pkg/app/server_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"flag"
2323
"fmt"
2424
"io"
25+
"net/http"
2526
"net/http/httptest"
2627
"os"
2728
"path/filepath"
@@ -444,6 +445,46 @@ kube_state_metrics_total_shards 1
444445
}
445446
}
446447

448+
func TestPprofRouting(t *testing.T) {
449+
t.Parallel()
450+
451+
pprofPaths := []string{
452+
"/debug/pprof/",
453+
"/debug/pprof/cmdline",
454+
"/debug/pprof/profile",
455+
"/debug/pprof/symbol",
456+
"/debug/pprof/trace",
457+
}
458+
459+
reg := prometheus.NewRegistry()
460+
telemetryMux := buildTelemetryServer(reg, false, nil)
461+
for _, path := range pprofPaths {
462+
req := httptest.NewRequest("GET", "http://localhost:8081"+path, nil)
463+
w := httptest.NewRecorder()
464+
telemetryMux.ServeHTTP(w, req)
465+
if w.Result().StatusCode == http.StatusNotFound {
466+
t.Errorf("expected pprof path %s to be registered on telemetry server, got 404", path)
467+
}
468+
}
469+
470+
opts := options.NewOptions()
471+
metricsMux := buildMetricsServer(
472+
metricshandler.New(opts, fake.NewSimpleClientset(), nil, false),
473+
prometheus.NewHistogramVec(prometheus.HistogramOpts{Name: "test_duration_seconds"}, []string{"method"}),
474+
fake.NewSimpleClientset(),
475+
false,
476+
nil,
477+
)
478+
for _, path := range pprofPaths {
479+
req := httptest.NewRequest("GET", "http://localhost:8080"+path, nil)
480+
w := httptest.NewRecorder()
481+
metricsMux.ServeHTTP(w, req)
482+
if w.Result().StatusCode != http.StatusNotFound {
483+
t.Errorf("expected pprof path %s to return 404 on metrics server, got %d", path, w.Result().StatusCode)
484+
}
485+
}
486+
}
487+
447488
// TestShardingEquivalenceScrapeCycle is a simple smoke test covering the entire cycle from
448489
// cache filling to scraping comparing a sharded with an unsharded setup.
449490
func TestShardingEquivalenceScrapeCycle(t *testing.T) {

0 commit comments

Comments
 (0)