fix(database): keep IP limits when the fail2ban probe is inconclusive - #6176
fix(database): keep IP limits when the fail2ban probe is inconclusive#6176n0ctal wants to merge 1 commit into
Conversation
be29a2c to
65e882a
Compare
|
Why the checks are red. One of them was mine and is now fixed. The first push left What remains is not from this PR:
Local gate on the branch: |
|
Follow-up on the two remaining red checks, with what I could verify from here.
I checked out
The same job is green on
Nothing in either check comes from this branch. |
65e882a to
89dcc16
Compare
ResetIpLimitNoFail2ban clears limitIp on every client — inbound settings JSON and the clients table — whenever fail2banCanEnforce() returns false, then records itself in the seeder history so it never re-evaluates. The probe was a single `fail2ban-client -h` run, so it answered false both when fail2ban is genuinely absent and when the command merely failed that once: a panel that starts before fail2ban is up, or in a container where it is installed a moment later, permanently loses every configured limit with no log line and no way back. Separate the two. A missing binary still means "absent" and the cleanup runs as before; a binary that exists but will not run is reported as unknown, leaves the configured values untouched, logs why, and does not record the seeder, so the next start decides again.
89dcc16 to
dfd4ed3
Compare
Summary
ResetIpLimitNoFail2banwiped every configuredlimitIpwheneverfail2ban-client -hfailed for any reason, including a transient one, and then recorded itself so the decision was never revisited. Distinguish "fail2ban is absent" from "the probe failed".Why
The seeder's intent is sound: with no fail2ban there is nothing to enforce
limitIp, so clearing it avoids a setting that silently does nothing.The problem is the evidence it acts on.
exec.Command("fail2ban-client", "-h").Run() == nilis false both when the binary does not exist and when it exists but that one run failed — a panel started before fail2ban is up, an image where fail2ban is installed a moment later, a momentarily unavailable exec.The consequence is not proportional to the uncertainty: it rewrites every inbound's settings JSON and every
clients.limit_ipto 0, recordsResetIpLimitNoFail2banin the seeder history, and returns. There is no log line on the destructive path, and because the history row exists the seeder never looks again. Operator configuration is gone with nothing to restore it from.Scope
internal/database/db.go—fail2banEnforcementState()returnsfail2banEnforcing/fail2banAbsent/fail2banUnknown; a missing binary (exec.LookPath) is still absent, a present-but-unrunnable one is unknown.fail2banCanEnforce()keeps its signature for existing callers.fail2banUnknown, logs the probe error, and does not record itself, so the next start re-evaluates. Behaviour on absent and on enforcing is unchanged.internal/database/fail2ban_state_test.go— covers all three states with a stubfail2ban-clientonPATH.Validation
go test ./internal/database/ -count=1green;go build ./...andgo vetclean.Risk
Low, and strictly less destructive than before. A host that genuinely lacks fail2ban still gets the cleanup on the same schedule. The only new outcome is that an inconclusive probe defers instead of deleting.