Serve Prometheus metrics behind SWWAF_METRICS_TOKEN (closes #23)
check / check (push) Successful in 4m12s
check / check (push) Successful in 4m12s
GET /_smallwebwaf/metrics answers in the Prometheus text format for a request carrying SWWAF_METRICS_TOKEN, 401 without it and 404 while it is unset. Every request under /_smallwebwaf/ but the health check now goes through the checks and is answered where it would be forwarded, 404 for any path but the metrics, so none reaches the app. SWWAF_METRICS_TOP_N bounds the series by country, the rest counted as other. Judgement call: a request answered at smallwebwaf's own endpoints is neither forwarded nor refused in the client's history. Deviation: go.mod and go.sum written by hand from the module proxy and sum.golang.org, as go runs only through make. Deviation: no metrics yet for state files read again after an edit or edits set aside; that work is not merged. Model: opus-5-5
This commit is contained in:
@@ -19,26 +19,29 @@ func TestHistoryKeepsEveryRequest(t *testing.T) {
|
||||
{Country: "DE", Forwarded: true, Status: 200, RequestBytes: 10, ResponseBytes: 100},
|
||||
{Forwarded: true, Status: 101},
|
||||
{Forwarded: true, Status: 304, RequestBytes: 5},
|
||||
{Country: "FR", Status: 403, ResponseBytes: 10, BrokeLimit: true},
|
||||
{Country: "FR", Refused: true, Status: 403, ResponseBytes: 10, BrokeLimit: true},
|
||||
{Forwarded: true, Status: 502, ResponseBytes: 12},
|
||||
// Closed without an answer: refused, and no response.
|
||||
{Status: 0},
|
||||
{Refused: true, Status: 0},
|
||||
// Answered at smallwebwaf's own endpoints: neither forwarded nor
|
||||
// refused.
|
||||
{Status: 404},
|
||||
} {
|
||||
limiter.AddToHistory(client, start.Add(time.Duration(i)*time.Minute), r)
|
||||
}
|
||||
|
||||
want := ratelimit.History{
|
||||
FirstSeen: start,
|
||||
LastSeen: start.Add(5 * time.Minute),
|
||||
LastSeen: start.Add(6 * time.Minute),
|
||||
Country: "FR",
|
||||
LookedUp: start.Add(3 * time.Minute),
|
||||
Requests: 6,
|
||||
Requests: 7,
|
||||
Forwarded: 4,
|
||||
Refused: 2,
|
||||
RequestBytes: 15,
|
||||
ResponseBytes: 122,
|
||||
Responses: ratelimit.Responses{
|
||||
Status1xx: 1, Status2xx: 1, Status3xx: 1, Status4xx: 1, Status5xx: 1,
|
||||
Status1xx: 1, Status2xx: 1, Status3xx: 1, Status4xx: 2, Status5xx: 1,
|
||||
},
|
||||
Offences: ratelimit.Offences{Limit: 1},
|
||||
}
|
||||
|
||||
@@ -71,7 +71,8 @@ type History struct {
|
||||
Country string `json:"country,omitempty"`
|
||||
LookedUp time.Time `json:"looked_up,omitzero"`
|
||||
// Requests are all the client's requests: Forwarded those passed to
|
||||
// the app, Refused those refused before anything reached it.
|
||||
// the app, Refused those refused before anything reached it, and
|
||||
// neither those smallwebwaf answered at its own endpoints.
|
||||
Requests int64 `json:"requests"`
|
||||
Forwarded int64 `json:"forwarded"`
|
||||
Refused int64 `json:"refused"`
|
||||
@@ -103,9 +104,11 @@ type Offences struct {
|
||||
type Request struct {
|
||||
// Country is the client's country, when the request looked it up.
|
||||
Country string
|
||||
// Forwarded is true for a request passed to the app, false for one
|
||||
// refused before anything reached it.
|
||||
// Forwarded is true for a request passed to the app, Refused for one
|
||||
// refused before anything reached it. Both are false for a request
|
||||
// smallwebwaf answered at its own endpoints.
|
||||
Forwarded bool
|
||||
Refused bool
|
||||
// Status is what the client was sent, 0 if nothing was.
|
||||
Status int
|
||||
// RequestBytes and ResponseBytes are the body bytes of the request
|
||||
@@ -199,7 +202,9 @@ func (l *Limiter) AddToHistory(client netip.Prefix, now time.Time, r Request) {
|
||||
h.Requests++
|
||||
if r.Forwarded {
|
||||
h.Forwarded++
|
||||
} else {
|
||||
}
|
||||
|
||||
if r.Refused {
|
||||
h.Refused++
|
||||
}
|
||||
|
||||
@@ -235,6 +240,14 @@ func (l *Limiter) Requests(netblock netip.Prefix) int64 {
|
||||
return requests
|
||||
}
|
||||
|
||||
// Len returns how many clients are in the table.
|
||||
func (l *Limiter) Len() int {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
return l.clients.Len()
|
||||
}
|
||||
|
||||
// Snapshot returns every client in the table, sorted by address, as
|
||||
// clients.json lists them.
|
||||
func (l *Limiter) Snapshot() []Client {
|
||||
|
||||
Reference in New Issue
Block a user