Bans an admin makes or lifts: the admin cause, a reason, lifted bans kept (closes #86)
check / check (push) Successful in 3m27s
check / check (push) Successful in 3m27s
A bans.json entry without a cause gets the cause admin, written back so. Bans whose cause is admin are never dropped and do not count toward SWWAF_MAX_BANS, so setting a ban's cause to admin keeps it. Bans smallwebwaf makes get a reason: the limit broken or the rule matched. A lifted ban refuses nothing, is kept, and makes no later ban longer. smallwebwaf_bans_made_total counts admin bans an edit adds while running; earlier_bans counts admin in place of without_cause. Judgement call: lifted lifts at once, whatever time it gives. Judgement call: a lifted ban still counts in earlier_bans. Known gap: a ban dropped from behind an admin's ban on its netblock leaves that netblock's later earlier_bans. Model: opus-5-5
This commit was merged in pull request #88.
This commit is contained in:
+36
-14
@@ -48,7 +48,7 @@ var (
|
||||
errVersion = errors.New("unknown version")
|
||||
// errMissing is for an entry without a field it needs.
|
||||
errMissing = errors.New("has no")
|
||||
errCause = errors.New("is not limit or attack")
|
||||
errCause = errors.New("is not limit, attack or admin")
|
||||
)
|
||||
|
||||
// Params are what Load needs.
|
||||
@@ -96,12 +96,15 @@ type bansFile struct {
|
||||
}
|
||||
|
||||
// banEntry is a ban as bans.json holds it: a permanent ban's expires is
|
||||
// null, and a ban an admin added may have no cause.
|
||||
// null, a ban an admin added may have no cause, which makes it an
|
||||
// admin's, and lifted is left out until an admin lifts the ban.
|
||||
type banEntry struct {
|
||||
Netblock netip.Prefix `json:"netblock"`
|
||||
Start time.Time `json:"start"`
|
||||
Expires *time.Time `json:"expires"`
|
||||
Cause string `json:"cause,omitempty"`
|
||||
Cause string `json:"cause"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Lifted *time.Time `json:"lifted,omitempty"`
|
||||
Notes bans.Notes `json:"notes"`
|
||||
}
|
||||
|
||||
@@ -262,7 +265,7 @@ func (f *Files) fileChanged(name string) {
|
||||
// runs, by Watch or by a write, is taken in here. An edit that does not
|
||||
// parse is neither counted nor logged, and takeIn's error returned.
|
||||
func (f *Files) takeInEdit(name string, data []byte) error {
|
||||
_, err := f.takeIn(name, data)
|
||||
_, err := f.takeIn(name, data, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -284,7 +287,7 @@ func (f *Files) read(name string) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return f.takeIn(name, data)
|
||||
return f.takeIn(name, data, false)
|
||||
}
|
||||
|
||||
// readChanged returns what the state file name holds, and whether that
|
||||
@@ -308,9 +311,11 @@ func (f *Files) readChanged(name string) ([]byte, bool, error) {
|
||||
|
||||
// takeIn parses data, what the state file name holds, puts it into the
|
||||
// part that keeps that state, in place of what the part held, and returns
|
||||
// how many entries the file holds. An error names the file and, where the
|
||||
// JSON decoder tells it, the line and column, or else the entry.
|
||||
func (f *Files) takeIn(name string, data []byte) (int, error) {
|
||||
// how many entries the file holds. edit is whether data is an admin's
|
||||
// edit taken in while smallwebwaf runs, rather than the file read at the
|
||||
// start. An error names the file and, where the JSON decoder tells it,
|
||||
// the line and column, or else the entry.
|
||||
func (f *Files) takeIn(name string, data []byte, edit bool) (int, error) {
|
||||
path := filepath.Join(f.params.Dir, name)
|
||||
|
||||
var entries int
|
||||
@@ -329,7 +334,12 @@ func (f *Files) takeIn(name string, data []byte) (int, error) {
|
||||
held = append(held, entry.ban())
|
||||
}
|
||||
|
||||
f.params.Ledger.Load(held)
|
||||
if edit {
|
||||
f.params.Ledger.LoadEdit(held)
|
||||
} else {
|
||||
f.params.Ledger.Load(held)
|
||||
}
|
||||
|
||||
entries = len(held)
|
||||
case clientsJSON:
|
||||
var file clientsFile
|
||||
@@ -447,22 +457,34 @@ func (f *Files) encode(name string) ([]byte, error) {
|
||||
// newBanEntry returns ban as bans.json holds it.
|
||||
func newBanEntry(ban bans.Ban) banEntry {
|
||||
entry := banEntry{
|
||||
Netblock: ban.Netblock, Start: ban.Start, Cause: ban.Cause, Notes: ban.Notes,
|
||||
Netblock: ban.Netblock, Start: ban.Start, Cause: ban.Cause, Reason: ban.Reason,
|
||||
Notes: ban.Notes,
|
||||
}
|
||||
if !ban.Permanent() {
|
||||
entry.Expires = &ban.Expires
|
||||
}
|
||||
|
||||
if !ban.Lifted.IsZero() {
|
||||
entry.Lifted = &ban.Lifted
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
// ban returns the ban an entry of bans.json holds.
|
||||
func (e banEntry) ban() bans.Ban {
|
||||
ban := bans.Ban{Netblock: e.Netblock, Start: e.Start, Cause: e.Cause, Notes: e.Notes}
|
||||
ban := bans.Ban{
|
||||
Netblock: e.Netblock, Start: e.Start, Cause: e.Cause, Reason: e.Reason,
|
||||
Notes: e.Notes,
|
||||
}
|
||||
if e.Expires != nil {
|
||||
ban.Expires = *e.Expires
|
||||
}
|
||||
|
||||
if e.Lifted != nil {
|
||||
ban.Lifted = *e.Lifted
|
||||
}
|
||||
|
||||
return ban
|
||||
}
|
||||
|
||||
@@ -470,8 +492,8 @@ func (e banEntry) ban() bans.Ban {
|
||||
// client, a start, from which the length of the netblock's next ban is
|
||||
// worked out, or an expires, which would make it permanent. A permanent
|
||||
// ban's expires is null, which Bans cannot tell from a missing one, so
|
||||
// each expires is read again as written. A cause other than limit or
|
||||
// attack, most likely misspelt, is refused too.
|
||||
// each expires is read again as written. A cause other than limit,
|
||||
// attack or admin, most likely misspelt, is refused too.
|
||||
func (f *bansFile) check(data []byte) error {
|
||||
var written struct {
|
||||
Bans []struct {
|
||||
@@ -493,7 +515,7 @@ func (f *bansFile) check(data []byte) error {
|
||||
case written.Bans[i].Expires == nil:
|
||||
return missing(i, "expires")
|
||||
case entry.Cause != "" && entry.Cause != bans.CauseLimit &&
|
||||
entry.Cause != bans.CauseAttack:
|
||||
entry.Cause != bans.CauseAttack && entry.Cause != bans.CauseAdmin:
|
||||
return fmt.Errorf("entry %d's cause %q %w", i+1, entry.Cause, errCause)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user