hub v0.148.0 (code): SECURITY — the customer list and box reports need the operator key (R-951); R-950 rotated; R-952, R-953 filed (97 -> 99 rows)
gates / gates (push) Successful in 6m8s

This commit is contained in:
2026-10-12 16:28:33 +02:00
parent fa4c505a55
commit b67b8cea8f
6 changed files with 142 additions and 4 deletions
+29
View File
@@ -230,6 +230,17 @@ func (h *Handler) checkAuthHost(r *http.Request) (hostID, customerID string, isG
return host.HostID, host.CustomerID, false, true
}
// requireGlobalKey answers 403 and returns false unless the request carries the operator/global key.
// The same check as the /admin/wg/* handlers (wg.go), in one place for routes that read every customer.
func (h *Handler) requireGlobalKey(w http.ResponseWriter, r *http.Request) bool {
_, _, isGlobal, ok := h.checkAuthHost(r)
if !ok || !isGlobal {
http.Error(w, "Forbidden: global key required", http.StatusForbidden)
return false
}
return true
}
// ServeHTTP routes API requests.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := strings.TrimPrefix(r.URL.Path, "/api/v1")
@@ -2415,6 +2426,12 @@ func (h *Handler) handleEvent(w http.ResponseWriter, r *http.Request) {
}
func (h *Handler) handleCustomers(w http.ResponseWriter, r *http.Request) {
// R-951: operator/global key only. These routes return every customer's name, dashboard address and
// stored box report; until v0.148.0 they answered with no credential at all. A box's own key must not
// list customers either (TestCustomersRoutes_*).
if !h.requireGlobalKey(w, r) {
return
}
customers, err := h.store.GetCustomers()
if err != nil {
h.logger.Printf("[ERROR] Failed to get customers: %v", err)
@@ -2458,6 +2475,12 @@ func (h *Handler) handleCustomers(w http.ResponseWriter, r *http.Request) {
}
func (h *Handler) handleCustomer(w http.ResponseWriter, r *http.Request, customerID string) {
// R-951: operator/global key only. These routes return every customer's name, dashboard address and
// stored box report; until v0.148.0 they answered with no credential at all. A box's own key must not
// list customers either (TestCustomersRoutes_*).
if !h.requireGlobalKey(w, r) {
return
}
customer, err := h.store.GetCustomer(customerID)
if err != nil {
h.logger.Printf("[ERROR] Failed to get customer %s: %v", customerID, err)
@@ -2475,6 +2498,12 @@ func (h *Handler) handleCustomer(w http.ResponseWriter, r *http.Request, custome
}
func (h *Handler) handleCustomerHistory(w http.ResponseWriter, r *http.Request, customerID string) {
// R-951: operator/global key only. These routes return every customer's name, dashboard address and
// stored box report; until v0.148.0 they answered with no credential at all. A box's own key must not
// list customers either (TestCustomersRoutes_*).
if !h.requireGlobalKey(w, r) {
return
}
period := r.URL.Query().Get("period")
var since time.Duration
switch period {