Serve Prometheus metrics behind SWWAF_METRICS_TOKEN (closes #23)
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:
2026-10-06 08:27:11 +00:00
parent 68f687cb0c
commit 2776bb4b09
23 changed files with 1459 additions and 66 deletions
+15 -3
View File
@@ -21,6 +21,7 @@ import (
"sneak.berlin/go/smallwebwaf/internal/bans"
"sneak.berlin/go/smallwebwaf/internal/lookup"
"sneak.berlin/go/smallwebwaf/internal/metrics"
"sneak.berlin/go/smallwebwaf/internal/ratelimit"
)
@@ -62,6 +63,8 @@ type Params struct {
Now func() time.Time
// ProcessLog receives what was read, and the writes that fail.
ProcessLog *slog.Logger
// Metrics count each file's writes.
Metrics *metrics.Metrics
}
// Files are the state files of a running smallwebwaf.
@@ -204,7 +207,7 @@ func (f *Files) writeBans() error {
return fmt.Errorf("encode %s: %w", bansJSON, err)
}
return write(f.params.Dir, bansJSON, append(data, '\n'))
return f.writeCounted(bansJSON, append(data, '\n'))
}
// writeClients writes clients.json.
@@ -214,7 +217,7 @@ func (f *Files) writeClients() error {
return fmt.Errorf("encode %s: %w", clientsJSON, err)
}
return write(f.params.Dir, clientsJSON, data)
return f.writeCounted(clientsJSON, data)
}
// writeLookups writes lookups.json.
@@ -224,7 +227,16 @@ func (f *Files) writeLookups() error {
return fmt.Errorf("encode %s: %w", lookupsJSON, err)
}
return write(f.params.Dir, lookupsJSON, data)
return f.writeCounted(lookupsJSON, data)
}
// writeCounted writes data to the state file name, as write does, and
// counts the write in the metrics.
func (f *Files) writeCounted(name string, data []byte) error {
err := write(f.params.Dir, name, data)
f.params.Metrics.StateFileWritten(name, len(data), err)
return err
}
// newBanEntry returns ban as bans.json holds it.