P2.6: закрыть /metrics Bearer admin-токеном
/metrics отдаёт per-tenant данные (accessKey, число очередей и сообщений), что является утечкой наружу на публичном endpoint. Теперь /metrics защищён тем же Bearer admin-токеном (timing-safe сравнение). Скрепперу VM токен задаётся в конфиге.
This commit is contained in:
+21
-2
@@ -4,6 +4,7 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
@@ -41,8 +42,10 @@ func New(tenantStore *tenant.TenantStore, adminToken string) http.Handler {
|
||||
// /static — логотип, favicon для публичных страниц
|
||||
r.PathPrefix("/static").Handler(ui.StaticHandler()).Methods("GET")
|
||||
|
||||
// /metrics — Prometheus endpoint для Victoria Metrics scrape
|
||||
r.Handle("/metrics", promhttp.Handler()).Methods("GET")
|
||||
// /metrics — Prometheus endpoint для Victoria Metrics scrape.
|
||||
// Защищён Bearer admin-токеном: метрики содержат per-tenant данные
|
||||
// (accessKey/число очередей и сообщений), которые нельзя светить наружу.
|
||||
r.Handle("/metrics", metricsAuth(adminToken, promhttp.Handler())).Methods("GET")
|
||||
|
||||
// Страница примеров команд — публично
|
||||
r.HandleFunc("/examples", func(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -181,6 +184,22 @@ func health(w http.ResponseWriter, req *http.Request) {
|
||||
fmt.Fprintf(w, `{"status":"ok","version":%q}`, models.Version)
|
||||
}
|
||||
|
||||
// metricsAuth — оборачивает /metrics в проверку Bearer admin-токена.
|
||||
// В отличие от публичного /health, метрики содержат per-tenant данные
|
||||
// (accessKey, число очередей/сообщений) и не должны быть доступны без auth.
|
||||
func metricsAuth(adminToken string, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
expected := "Bearer " + adminToken
|
||||
if len(authHeader) != len(expected) ||
|
||||
subtle.ConstantTimeCompare([]byte(authHeader), []byte(expected)) != 1 {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func actionHandler(w http.ResponseWriter, req *http.Request) {
|
||||
action := extractAction(req)
|
||||
start := time.Now()
|
||||
|
||||
Reference in New Issue
Block a user