Add 80% limit warning, stop repeat notifications, fix review findings

Notifications:
- Warn once when a counter reaches warn_percent of its limit, through
  separate warn_*_url endpoints.
- Write ntf/ markers so a lost Redis reply cannot re-send a breach.
- Retry only when the request never left or the web app answered 429/5xx.

Fixes:
- Retry transactions Redis rejected; treat pool timeouts as definite.
- Install the signal handler before startup work.
- Stop logging the login suffix; redact notify URL credentials.
- Reject corrupt or empty CDBs, truncated hash tables included.
- Require a Sentinel password, refuse example placeholders, and default
  admin_addr to localhost.
- Normalise /counters domains, count on the arrival day, log rejections,
  hide internals in errors and exit 1 on a failed drain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dimitrije Dimitrijevic
2026-11-09 09:01:56 +01:00
co-authored by Claude Opus 5.5
parent c43ff17627
commit 54f9626121
29 changed files with 1673 additions and 245 deletions
+32 -1
View File
@@ -22,7 +22,7 @@ func TestValidateOut(t *testing.T) {
// Support/token logins (review #3): mizu forwards the raw SASL login
// user@dom@SUFFIX; the base address is counted, the suffix logged.
{"login suffix", OutRequest{AuthUser: "x@yagolivas.com@SUPPORTTOKEN", Recipient: "y@example.com"}, "",
ValidatedOut{AuthUser: Address{Value: "x@yagolivas.com"}, LoginSuffix: "SUPPORTTOKEN", Recipient: Address{Value: "y@example.com"}}},
ValidatedOut{AuthUser: Address{Value: "x@yagolivas.com"}, TokenLogin: true, Recipient: Address{Value: "y@example.com"}}},
// mail_from never rejects (review #2): kept normalised when possible,
// otherwise as a sanitised string for the log.
{"mail_from empty", OutRequest{AuthUser: "x@example.com", Recipient: "y@example.com", MailFrom: ""}, "",
@@ -253,3 +253,34 @@ func TestErrorResponseJSON(t *testing.T) {
t.Errorf("got %s", b)
}
}
func TestNormalizeDomain(t *testing.T) {
tests := []struct{ in, want string }{
{"Example.COM", "example.com"},
{" example.com. ", "example.com"},
{"bücher.de", "xn--bcher-kva.de"},
{"xn--bcher-kva.de", "xn--bcher-kva.de"},
}
for _, tt := range tests {
got, err := NormalizeDomain(tt.in)
if err != nil || got != tt.want {
t.Errorf("NormalizeDomain(%q) = %q, %v; want %q", tt.in, got, err, tt.want)
}
}
for _, bad := range []string{"", ".", "a..b", ".a", "host_name.example.com", "[192.0.2.1]", "a b.com"} {
if got, err := NormalizeDomain(bad); err == nil {
t.Errorf("NormalizeDomain(%q) = %q, want an error", bad, got)
}
}
}
// An unusable login is logged, but never with what follows a second "@".
func TestValidateOut_UnusableLoginDropsSuffix(t *testing.T) {
v, err := ValidateOut(OutRequest{AuthUser: "x@bad..domain@SECRET", Recipient: "y@example.com"})
if err != nil {
t.Fatal(err)
}
if v.AuthUser.Usable() || v.AuthUser.Raw != "x@bad..domain" {
t.Errorf("AuthUser = %+v, want the raw value without the suffix", v.AuthUser)
}
}