Trap paths, and the error burst banning a client refused too often (closes #115)
check / check (push) Waiting to run
check / check (push) Waiting to run
SWWAF_TRAP_PATHS: a request whose path, as a path rule sees it, is one of them is a clear sign of attack, banned as a ban rule's match is; the ban's notes give its trap_path. Checked after the rate limits, before the rule files. SWWAF_ERROR_BURST_THRESHOLD (default 30, or off): more refusals in a minute after a block or ban rule or a trap path, or for a missing or wrong token, ban the client as a broken limit does. Counted in clients.json's minute_refusals; limit_hit error_burst, notes kind refusals. A token refusal is now the offence token_refused, and smallwebwaf_offences_total counts every kind the history does. Judgement call: the threshold is not lowered by a client's limit percentage. Model: opus-5-5
This commit was merged in pull request #118.
This commit is contained in:
+24
-19
@@ -6,7 +6,6 @@ package metrics
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -94,8 +93,8 @@ func New(topN int, instanceName string) *Metrics {
|
||||
Help: "How long requests passed to the app took, from then to their end.",
|
||||
}),
|
||||
rateLimitHits: counterVec("smallwebwaf_rate_limit_hits_total",
|
||||
"Requests that broke a rate limit or a byte limit, by its window and "+
|
||||
"its kind, requests or bytes.",
|
||||
"Requests that broke a rate limit, a byte limit or the error burst, by "+
|
||||
"its window and its kind, requests, bytes or refusals.",
|
||||
[]string{"window", "kind"}),
|
||||
sizeAndTimeLimitHits: counterVec("smallwebwaf_size_and_time_limit_hits_total",
|
||||
"Requests that passed a size or time limit, by its setting.",
|
||||
@@ -407,26 +406,10 @@ func (m *Metrics) RequestEnded(
|
||||
m.upstreamDuration.Observe(upstreamDuration.Seconds())
|
||||
}
|
||||
|
||||
if line.LimitHit != "" {
|
||||
// The log line names a byte limit's window with _bytes after it.
|
||||
window, isBytes := strings.CutSuffix(line.LimitHit, "_bytes")
|
||||
|
||||
kind := ratelimit.KindRequests
|
||||
if isBytes {
|
||||
kind = ratelimit.KindBytes
|
||||
}
|
||||
|
||||
m.rateLimitHits.WithLabelValues(window, kind).Inc()
|
||||
}
|
||||
|
||||
if limit != "" {
|
||||
m.sizeAndTimeLimitHits.WithLabelValues(limit).Inc()
|
||||
}
|
||||
|
||||
if line.Offence != "" {
|
||||
m.offences.WithLabelValues(line.Offence).Inc()
|
||||
}
|
||||
|
||||
if line.Country != "" {
|
||||
m.countries.add(line.Country, line)
|
||||
}
|
||||
@@ -436,6 +419,28 @@ func (m *Metrics) RequestEnded(
|
||||
}
|
||||
}
|
||||
|
||||
// LimitHit counts a request that broke a rate limit, a byte limit or the
|
||||
// error burst, by the window and the kind of hit.
|
||||
func (m *Metrics) LimitHit(hit ratelimit.Hit) {
|
||||
m.rateLimitHits.WithLabelValues(hit.Window, hit.Kind).Inc()
|
||||
}
|
||||
|
||||
// Offences counts the offences of r, a request that has ended, as its
|
||||
// client's history counts them, by kind, named as clients.json names
|
||||
// them.
|
||||
func (m *Metrics) Offences(r ratelimit.Request) {
|
||||
for kind, committed := range map[string]bool{
|
||||
"limit": r.BrokeLimit,
|
||||
"attack": r.Attack,
|
||||
"rule_blocked": r.RuleBlocked,
|
||||
"token_refused": r.TokenRefused,
|
||||
} {
|
||||
if committed {
|
||||
m.offences.WithLabelValues(kind).Inc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RuleMatched counts a request that matched the rule id, whose action is
|
||||
// action.
|
||||
func (m *Metrics) RuleMatched(id, action string) {
|
||||
|
||||
Reference in New Issue
Block a user