DNS blocklists asked in the background, verdicts kept (closes #104)
check / check (push) Waiting to run
check / check (push) Waiting to run
Zones in SWWAF_DNSBL_ZONES are asked about each client in the background, through SWWAF_DNSBL_RESOLVER or the host's resolver; no request waits. Verdicts last SWWAF_REPUTATION_CACHE_TTL, kept in reputation.json. SWWAF_REPUTATION_ACTION (limit:25) denies, limits or logs a listed client; each zone listing it raises reputation_hit. A failed query gives no verdict, raises source_failure, and pauses the zone a minute. A zone's key, its first label under dq.spamhaus.net, is masked everywhere but reputation.json. Zones compare without regard to case. Judgement call: answers in 127.255.255.0/24 or outside 127.0.0.0/8 are failures. Judgement call: the minute's pause after a failure; at most 1,000 queries at once. Judgement call: one zone given with two keys stops the start as listed twice. Rule suppressed: paralleltest on the DNSBL tests (Go's resolver shares state across synctest bubbles), funlen on the test of every logged setting. Model: opus-5-5
This commit is contained in:
+42
-12
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"sneak.berlin/go/smallwebwaf/internal/alerts"
|
||||
"sneak.berlin/go/smallwebwaf/internal/bans"
|
||||
"sneak.berlin/go/smallwebwaf/internal/config"
|
||||
"sneak.berlin/go/smallwebwaf/internal/ratelimit"
|
||||
"sneak.berlin/go/smallwebwaf/internal/remotelog"
|
||||
"sneak.berlin/go/smallwebwaf/internal/reputation"
|
||||
@@ -256,24 +257,53 @@ func (m *Metrics) AddLookupFile(lastRead func() time.Time, readFailures func() i
|
||||
)
|
||||
}
|
||||
|
||||
// AddReputation adds the metrics of the lists fetched from URLs, by
|
||||
// source, each list's URL: the requests whose client a blocklist lists,
|
||||
// which ReputationHit counts, and, read from lists as the metrics are
|
||||
// asked for, the fetches that failed and when the copy in use was fetched.
|
||||
// It is called once, before ReputationHit.
|
||||
func (m *Metrics) AddReputation(lists *reputation.Lists) {
|
||||
// AddReputation adds the metrics of the lists fetched from URLs and of the
|
||||
// DNSBL zones, by source, each list's URL or each zone, its key masked as
|
||||
// config.MaskZoneKey masks it: the requests whose client a blocklist or a
|
||||
// zone's verdict lists, which ReputationHit counts, and, read from lists
|
||||
// and dnsbl as the metrics are asked for, for a list, the fetches that
|
||||
// failed and when the copy in use was fetched, and for a zone, the queries
|
||||
// made and those that failed. It is called once, before ReputationHit.
|
||||
func (m *Metrics) AddReputation(lists *reputation.Lists, dnsbl *reputation.DNSBL) {
|
||||
const (
|
||||
sourceLabel = "source"
|
||||
failuresHelp = "Fetches of the list, or queries to the DNSBL zone, that failed."
|
||||
)
|
||||
|
||||
m.reputationHits = counterVec("smallwebwaf_reputation_hits_total",
|
||||
"Requests whose client a blocklist lists, by the blocklist's URL.",
|
||||
[]string{"source"})
|
||||
"Requests whose client a blocklist or a DNSBL zone lists, by the "+
|
||||
"blocklist's URL or the zone.",
|
||||
[]string{sourceLabel})
|
||||
m.registry.MustRegister(m.reputationHits)
|
||||
|
||||
for _, zone := range dnsbl.Zones() {
|
||||
source := prometheus.Labels{sourceLabel: config.MaskZoneKey(zone)}
|
||||
|
||||
m.registry.MustRegister(
|
||||
prometheus.NewCounterFunc(prometheus.CounterOpts{
|
||||
Name: "smallwebwaf_reputation_queries_total",
|
||||
Help: "Queries to the DNSBL zone.",
|
||||
ConstLabels: source,
|
||||
}, func() float64 {
|
||||
return float64(dnsbl.Queries(zone))
|
||||
}),
|
||||
prometheus.NewCounterFunc(prometheus.CounterOpts{
|
||||
Name: "smallwebwaf_reputation_failures_total",
|
||||
Help: failuresHelp,
|
||||
ConstLabels: source,
|
||||
}, func() float64 {
|
||||
return float64(dnsbl.Failures(zone))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
for _, listURL := range lists.URLs() {
|
||||
source := prometheus.Labels{"source": listURL}
|
||||
source := prometheus.Labels{sourceLabel: listURL}
|
||||
|
||||
m.registry.MustRegister(
|
||||
prometheus.NewCounterFunc(prometheus.CounterOpts{
|
||||
Name: "smallwebwaf_reputation_failures_total",
|
||||
Help: "Fetches of the list that failed.",
|
||||
Help: failuresHelp,
|
||||
ConstLabels: source,
|
||||
}, func() float64 {
|
||||
return float64(lists.Failures(listURL))
|
||||
@@ -295,8 +325,8 @@ func (m *Metrics) AddReputation(lists *reputation.Lists) {
|
||||
}
|
||||
}
|
||||
|
||||
// ReputationHit counts a request whose client the blocklist at source, its
|
||||
// URL, lists.
|
||||
// ReputationHit counts a request whose client source lists: a blocklist,
|
||||
// by its URL, or a DNSBL zone, its key masked.
|
||||
func (m *Metrics) ReputationHit(source string) {
|
||||
m.reputationHits.WithLabelValues(source).Inc()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user