script/fmt now runs golines -m 77 after gofmt, repeating golines until it changes nothing, since one pass can leave a line the next splits. script/fmt-check fails when either would change a file. script/bootstrap installs golines v0.13.0 with go install, pinned to its commit, and checks the installed version with go version -m. The tree is reformatted. The longer lines this makes are fixed by hand: nolint directives moved onto their own line above the statement, one string split, and 20 functions over funlen's 80 lines shortened or split, RunWAF and limitPercentages among them. Model: opus-5-5
This commit is contained in:
1 parent
63e58e326b
commit
7f8a59de04
118 files changed
+9447
-2670
No files matched your search
+146
-42
@@ -36,9 +36,21 @@ func TestAdminsBansAreNeverDroppedAndDoNotCountTowardMaxBans(t *testing.T) {
|
||||
// then one with an admin's ban before a ban sww made: the one
|
||||
// ban counted toward MaxBans.
|
||||
ledger.Load([]bans.Ban{
|
||||
{Netblock: adminsOnly, Start: midnight().Add(-3 * time.Hour), Cause: bans.CauseAdmin},
|
||||
{Netblock: adminsOnly, Start: midnight().Add(-2 * time.Hour), Cause: bans.CauseAdmin},
|
||||
{Netblock: both, Start: midnight().Add(-time.Hour), Cause: bans.CauseAdmin},
|
||||
{
|
||||
Netblock: adminsOnly,
|
||||
Start: midnight().Add(-3 * time.Hour),
|
||||
Cause: bans.CauseAdmin,
|
||||
},
|
||||
{
|
||||
Netblock: adminsOnly,
|
||||
Start: midnight().Add(-2 * time.Hour),
|
||||
Cause: bans.CauseAdmin,
|
||||
},
|
||||
{
|
||||
Netblock: both,
|
||||
Start: midnight().Add(-time.Hour),
|
||||
Cause: bans.CauseAdmin,
|
||||
},
|
||||
{
|
||||
Netblock: both,
|
||||
Start: midnight(),
|
||||
@@ -50,15 +62,27 @@ func TestAdminsBansAreNeverDroppedAndDoNotCountTowardMaxBans(t *testing.T) {
|
||||
|
||||
// A new ban drops the ban sww made, and only that one.
|
||||
ledger.BanForLimit(second, midnight(), bans.Notes{})
|
||||
wantBans(t, ledger, map[netip.Prefix]int{adminsOnly: 2, both: 1, second: 1})
|
||||
wantBans(
|
||||
t,
|
||||
ledger,
|
||||
map[netip.Prefix]int{adminsOnly: 2, both: 1, second: 1},
|
||||
)
|
||||
|
||||
if ledger.Bans(both)[0].Cause != bans.CauseAdmin {
|
||||
t.Errorf("%s kept %+v, want the admin's ban", both, ledger.Bans(both))
|
||||
t.Errorf(
|
||||
"%s kept %+v, want the admin's ban",
|
||||
both,
|
||||
ledger.Bans(both),
|
||||
)
|
||||
}
|
||||
|
||||
// And the next drops that one.
|
||||
ledger.BanForLimit(third, midnight(), bans.Notes{})
|
||||
wantBans(t, ledger, map[netip.Prefix]int{adminsOnly: 2, both: 1, second: 0, third: 1})
|
||||
wantBans(
|
||||
t,
|
||||
ledger,
|
||||
map[netip.Prefix]int{adminsOnly: 2, both: 1, second: 0, third: 1},
|
||||
)
|
||||
}
|
||||
|
||||
func TestEarlierBansCountABanDroppedAfterAnAdminsBan(t *testing.T) {
|
||||
@@ -74,7 +98,11 @@ func TestEarlierBansCountABanDroppedAfterAnAdminsBan(t *testing.T) {
|
||||
// twice, each time after its last ban has ended.
|
||||
ledger.BanForAdmin(netblock, midnight(), midnight().Add(time.Minute),
|
||||
"probes for logins")
|
||||
first, _ := ledger.BanForLimit(netblock, midnight().Add(time.Minute), bans.Notes{})
|
||||
first, _ := ledger.BanForLimit(
|
||||
netblock,
|
||||
midnight().Add(time.Minute),
|
||||
bans.Notes{},
|
||||
)
|
||||
second, _ := ledger.BanForLimit(netblock, first.Expires, bans.Notes{})
|
||||
|
||||
// A ban on another netblock fills the ledger, and the netblock's first
|
||||
@@ -119,12 +147,21 @@ func TestReasonOfTheBansSwwMakes(t *testing.T) {
|
||||
|
||||
ledger := bans.New(defaultParams())
|
||||
|
||||
limit, _ := ledger.BanForLimit(netip.MustParsePrefix("203.0.113.1/32"), midnight(),
|
||||
bans.Notes{Kind: "requests", Limit: 1000, Window: "minute"})
|
||||
byteLimit, _ := ledger.BanForLimit(netip.MustParsePrefix("203.0.113.3/32"),
|
||||
midnight(), bans.Notes{Kind: "bytes", Limit: 10 << 30, Window: "hour"})
|
||||
attack, _ := ledger.BanForAttack(netip.MustParsePrefix("203.0.113.2/32"), midnight(),
|
||||
bans.Notes{RuleID: "git-dir", Target: "path"})
|
||||
limit, _ := ledger.BanForLimit(
|
||||
netip.MustParsePrefix("203.0.113.1/32"),
|
||||
midnight(),
|
||||
bans.Notes{Kind: "requests", Limit: 1000, Window: "minute"},
|
||||
)
|
||||
byteLimit, _ := ledger.BanForLimit(
|
||||
netip.MustParsePrefix("203.0.113.3/32"),
|
||||
midnight(),
|
||||
bans.Notes{Kind: "bytes", Limit: 10 << 30, Window: "hour"},
|
||||
)
|
||||
attack, _ := ledger.BanForAttack(
|
||||
netip.MustParsePrefix("203.0.113.2/32"),
|
||||
midnight(),
|
||||
bans.Notes{RuleID: "git-dir", Target: "path"},
|
||||
)
|
||||
|
||||
for _, tc := range []struct{ got, want string }{
|
||||
{limit.Reason, "requests per minute over the limit of 1000"},
|
||||
@@ -166,17 +203,25 @@ func TestLiftedBanForALimitRefusesNothingAndMakesNoBanLonger(t *testing.T) {
|
||||
ban, _ := ledger.BanForLimit(netblock, now, bans.Notes{})
|
||||
if ban.Expires.Sub(ban.Start) != time.Hour ||
|
||||
ban.Notes.EarlierBans != (bans.EarlierBans{Limit: 1}) {
|
||||
t.Errorf("the next ban lasts %s with earlier bans %+v, want 1h and 1 for a limit",
|
||||
ban.Expires.Sub(ban.Start), ban.Notes.EarlierBans)
|
||||
t.Errorf(
|
||||
"the next ban lasts %s with earlier bans %+v, want 1h and 1 for a limit",
|
||||
ban.Expires.Sub(ban.Start),
|
||||
ban.Notes.EarlierBans,
|
||||
)
|
||||
}
|
||||
|
||||
held := ledger.Bans(netblock)
|
||||
if len(held) != 2 || !reflect.DeepEqual(held[0], lifted) {
|
||||
t.Errorf("the ledger holds %+v, want the lifted ban and the new one", held)
|
||||
t.Errorf(
|
||||
"the ledger holds %+v, want the lifted ban and the new one",
|
||||
held,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLiftedBanForAnAttackRefusesNothingAndMakesNoBanLonger(t *testing.T) {
|
||||
func TestLiftedBanForAnAttackRefusesNothingAndMakesNoBanLonger(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
// A permanent ban for a clear sign of attack, lifted.
|
||||
@@ -198,13 +243,20 @@ func TestLiftedBanForAnAttackRefusesNothingAndMakesNoBanLonger(t *testing.T) {
|
||||
|
||||
active, permanent := ledger.Count(now)
|
||||
if active != 0 || permanent != 0 {
|
||||
t.Errorf("%d bans are active and %d permanent, want none", active, permanent)
|
||||
t.Errorf(
|
||||
"%d bans are active and %d permanent, want none",
|
||||
active,
|
||||
permanent,
|
||||
)
|
||||
}
|
||||
|
||||
// The next clear sign of attack bans for seven days, as a first does.
|
||||
ban, _ := ledger.BanForAttack(netblock, now, bans.Notes{})
|
||||
if ban.Expires.Sub(ban.Start) != 7*day {
|
||||
t.Errorf("the next ban for an attack ends at %s, want seven days on", ban.Expires)
|
||||
t.Errorf(
|
||||
"the next ban for an attack ends at %s, want seven days on",
|
||||
ban.Expires,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,8 +264,11 @@ func TestLoadEditCountsTheBansAnAdminMade(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ledger := bans.New(defaultParams())
|
||||
made, _ := ledger.BanForLimit(netip.MustParsePrefix("203.0.113.1/32"), midnight(),
|
||||
bans.Notes{})
|
||||
made, _ := ledger.BanForLimit(
|
||||
netip.MustParsePrefix("203.0.113.1/32"),
|
||||
midnight(),
|
||||
bans.Notes{},
|
||||
)
|
||||
atStart := bans.Ban{
|
||||
Netblock: netip.MustParsePrefix("203.0.113.2/32"),
|
||||
Start: midnight(),
|
||||
@@ -223,7 +278,10 @@ func TestLoadEditCountsTheBansAnAdminMade(t *testing.T) {
|
||||
ledger.Load([]bans.Ban{made, atStart})
|
||||
|
||||
if got := ledger.Made(bans.CauseAdmin); got != 0 {
|
||||
t.Fatalf("%d bans made by an admin after the start's, want none", got)
|
||||
t.Fatalf(
|
||||
"%d bans made by an admin after the start's, want none",
|
||||
got,
|
||||
)
|
||||
}
|
||||
|
||||
// The admin keeps the ban sww made, keeps the one read at the
|
||||
@@ -236,9 +294,13 @@ func TestLoadEditCountsTheBansAnAdminMade(t *testing.T) {
|
||||
}
|
||||
ledger.LoadEdit([]bans.Ban{kept, atStart, added})
|
||||
|
||||
if ledger.Made(bans.CauseAdmin) != 1 || ledger.Made(bans.CauseLimit) != 1 {
|
||||
t.Errorf("%d bans made by an admin and %d for a limit, want 1 of each",
|
||||
ledger.Made(bans.CauseAdmin), ledger.Made(bans.CauseLimit))
|
||||
if ledger.Made(bans.CauseAdmin) != 1 ||
|
||||
ledger.Made(bans.CauseLimit) != 1 {
|
||||
t.Errorf(
|
||||
"%d bans made by an admin and %d for a limit, want 1 of each",
|
||||
ledger.Made(bans.CauseAdmin),
|
||||
ledger.Made(bans.CauseLimit),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,8 +326,12 @@ func TestAdminsBanIsMadeWhileAnotherLasts(t *testing.T) {
|
||||
Notes: bans.Notes{EarlierBans: bans.EarlierBans{Limit: 1}},
|
||||
}
|
||||
|
||||
got := ledger.BanForAdmin(netip.MustParsePrefix("203.0.113.9/24"), now, time.Time{},
|
||||
"probes for logins")
|
||||
got := ledger.BanForAdmin(
|
||||
netip.MustParsePrefix("203.0.113.9/24"),
|
||||
now,
|
||||
time.Time{},
|
||||
"probes for logins",
|
||||
)
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("the admin's ban is\n%+v\nwant\n%+v", got, want)
|
||||
}
|
||||
@@ -277,10 +343,17 @@ func TestAdminsBanIsMadeWhileAnotherLasts(t *testing.T) {
|
||||
}
|
||||
|
||||
// It refuses once the ban for the limit has ended.
|
||||
ban, banned, _ := ledger.Find(netblock.Addr(), midnight().Add(2*time.Hour))
|
||||
ban, banned, _ := ledger.Find(
|
||||
netblock.Addr(),
|
||||
midnight().Add(2*time.Hour),
|
||||
)
|
||||
if !banned || !reflect.DeepEqual(ban, want) {
|
||||
t.Errorf("after the limit's ban the netblock is under %+v (%t), want %+v",
|
||||
ban, banned, want)
|
||||
t.Errorf(
|
||||
"after the limit's ban the netblock is under %+v (%t), want %+v",
|
||||
ban,
|
||||
banned,
|
||||
want,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,8 +385,12 @@ func TestLiftLiftsEveryActiveBanCoveringTheClient(t *testing.T) {
|
||||
now := midnight().Add(time.Minute)
|
||||
|
||||
lifted := ledger.Lift(client, now)
|
||||
if len(lifted) != 2 || lifted[0].Lifted != now || lifted[1].Lifted != now {
|
||||
t.Errorf("lifted %+v, want the two active bans covering the client", lifted)
|
||||
if len(lifted) != 2 || lifted[0].Lifted != now ||
|
||||
lifted[1].Lifted != now {
|
||||
t.Errorf(
|
||||
"lifted %+v, want the two active bans covering the client",
|
||||
lifted,
|
||||
)
|
||||
}
|
||||
|
||||
wantChanged(t, ledger, true)
|
||||
@@ -330,8 +407,11 @@ func TestLiftLiftsEveryActiveBanCoveringTheClient(t *testing.T) {
|
||||
covering := ledger.Covering(client)
|
||||
if len(covering) != 3 || covering[0].Netblock != wide ||
|
||||
!covering[1].Lifted.IsZero() || covering[2].Lifted != now {
|
||||
t.Errorf("the bans covering the client are %+v, want the /24's and both "+
|
||||
"of its own, the earlier not lifted", covering)
|
||||
t.Errorf(
|
||||
"the bans covering the client are %+v, want the /24's and both "+
|
||||
"of its own, the earlier not lifted",
|
||||
covering,
|
||||
)
|
||||
}
|
||||
|
||||
// With none active, nothing is lifted or changed.
|
||||
@@ -342,16 +422,26 @@ func TestLiftLiftsEveryActiveBanCoveringTheClient(t *testing.T) {
|
||||
wantChanged(t, ledger, false)
|
||||
}
|
||||
|
||||
func TestPermanentBanOnAWiderNetblockIsReturnedOverATemporaryBan(t *testing.T) {
|
||||
func TestPermanentBanOnAWiderNetblockIsReturnedOverATemporaryBan(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
client := netip.MustParseAddr("203.0.113.9")
|
||||
ledger := bans.New(defaultParams())
|
||||
|
||||
// The temporary ban is made first, so that its netblock is looked at first.
|
||||
ledger.BanForLimit(netip.MustParsePrefix("203.0.113.9/32"), midnight(), bans.Notes{})
|
||||
ledger.BanForAdmin(netip.MustParsePrefix("203.0.113.0/24"), midnight(), time.Time{},
|
||||
"for good")
|
||||
ledger.BanForLimit(
|
||||
netip.MustParsePrefix("203.0.113.9/32"),
|
||||
midnight(),
|
||||
bans.Notes{},
|
||||
)
|
||||
ledger.BanForAdmin(
|
||||
netip.MustParsePrefix("203.0.113.0/24"),
|
||||
midnight(),
|
||||
time.Time{},
|
||||
"for good",
|
||||
)
|
||||
|
||||
wantPermanentBan(t, ledger, client, midnight().Add(time.Minute))
|
||||
}
|
||||
@@ -363,10 +453,19 @@ func TestOlderPermanentBanIsReturnedOverANewerTemporaryBan(t *testing.T) {
|
||||
ledger := bans.New(defaultParams())
|
||||
|
||||
ledger.BanForAdmin(netblock, midnight(), time.Time{}, "for good")
|
||||
ledger.BanForAdmin(netblock, midnight().Add(time.Minute), midnight().Add(time.Hour),
|
||||
"for an hour")
|
||||
ledger.BanForAdmin(
|
||||
netblock,
|
||||
midnight().Add(time.Minute),
|
||||
midnight().Add(time.Hour),
|
||||
"for an hour",
|
||||
)
|
||||
|
||||
wantPermanentBan(t, ledger, netblock.Addr(), midnight().Add(2*time.Minute))
|
||||
wantPermanentBan(
|
||||
t,
|
||||
ledger,
|
||||
netblock.Addr(),
|
||||
midnight().Add(2*time.Minute),
|
||||
)
|
||||
}
|
||||
|
||||
// wantPermanentBan checks that Find and Check return a permanent ban for
|
||||
@@ -382,7 +481,12 @@ func wantPermanentBan(
|
||||
} {
|
||||
ban, banned, _ := look(client, now)
|
||||
if !banned || !ban.Permanent() {
|
||||
t.Errorf("%s returned %+v and banned %v, want a permanent ban", name, ban, banned)
|
||||
t.Errorf(
|
||||
"%s returned %+v and banned %v, want a permanent ban",
|
||||
name,
|
||||
ban,
|
||||
banned,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
-19
@@ -209,7 +209,10 @@ func New(params Params) *Ledger {
|
||||
// The ledger drops bans itself, and never those whose cause is
|
||||
// CauseAdmin, however many there are, so the LRU has no limit of its
|
||||
// own: it keeps the netblocks in the order they were last seen.
|
||||
netblocks, err := simplelru.NewLRU[netip.Prefix, *[]Ban](math.MaxInt, nil)
|
||||
netblocks, err := simplelru.NewLRU[netip.Prefix, *[]Ban](
|
||||
math.MaxInt,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err) // NewLRU fails only for a size below one
|
||||
}
|
||||
@@ -312,7 +315,15 @@ func activeBan(bans []Ban, now time.Time) *Ban {
|
||||
func (l *Ledger) BanForLimit(
|
||||
netblock netip.Prefix, now time.Time, notes Notes,
|
||||
) (Ban, bool) {
|
||||
return l.ban(netblock, now, time.Time{}, CauseLimit, limitReason(notes), notes, true)
|
||||
return l.ban(
|
||||
netblock,
|
||||
now,
|
||||
time.Time{},
|
||||
CauseLimit,
|
||||
limitReason(notes),
|
||||
notes,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
// WouldBanForLimit returns what BanForLimit would, without making the ban:
|
||||
@@ -320,7 +331,15 @@ func (l *Ledger) BanForLimit(
|
||||
func (l *Ledger) WouldBanForLimit(
|
||||
netblock netip.Prefix, now time.Time, notes Notes,
|
||||
) (Ban, bool) {
|
||||
return l.ban(netblock, now, time.Time{}, CauseLimit, limitReason(notes), notes, false)
|
||||
return l.ban(
|
||||
netblock,
|
||||
now,
|
||||
time.Time{},
|
||||
CauseLimit,
|
||||
limitReason(notes),
|
||||
notes,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
// BanForAttack bans netblock at now for a clear sign of attack, with
|
||||
@@ -331,7 +350,15 @@ func (l *Ledger) WouldBanForLimit(
|
||||
func (l *Ledger) BanForAttack(
|
||||
netblock netip.Prefix, now time.Time, notes Notes,
|
||||
) (Ban, bool) {
|
||||
return l.ban(netblock, now, time.Time{}, CauseAttack, attackReason(notes), notes, true)
|
||||
return l.ban(
|
||||
netblock,
|
||||
now,
|
||||
time.Time{},
|
||||
CauseAttack,
|
||||
attackReason(notes),
|
||||
notes,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
// WouldBanForAttack returns what BanForAttack would, without making the
|
||||
@@ -339,8 +366,15 @@ func (l *Ledger) BanForAttack(
|
||||
func (l *Ledger) WouldBanForAttack(
|
||||
netblock netip.Prefix, now time.Time, notes Notes,
|
||||
) (Ban, bool) {
|
||||
return l.ban(netblock, now, time.Time{}, CauseAttack, attackReason(notes), notes,
|
||||
false)
|
||||
return l.ban(
|
||||
netblock,
|
||||
now,
|
||||
time.Time{},
|
||||
CauseAttack,
|
||||
attackReason(notes),
|
||||
notes,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
// BanForCrowdSec bans netblock at now until expires, when CrowdSec's
|
||||
@@ -348,19 +382,39 @@ func (l *Ledger) WouldBanForAttack(
|
||||
// whether it made it, as BanForLimit does. Its reason is "CrowdSec's
|
||||
// decision for <scenario>", the scenario that made the decision.
|
||||
func (l *Ledger) BanForCrowdSec(
|
||||
netblock netip.Prefix, now, expires time.Time, scenario string, notes Notes,
|
||||
netblock netip.Prefix,
|
||||
now, expires time.Time,
|
||||
scenario string,
|
||||
notes Notes,
|
||||
) (Ban, bool) {
|
||||
return l.ban(netblock, now, expires, CauseCrowdSec, crowdSecReason(scenario), notes,
|
||||
true)
|
||||
return l.ban(
|
||||
netblock,
|
||||
now,
|
||||
expires,
|
||||
CauseCrowdSec,
|
||||
crowdSecReason(scenario),
|
||||
notes,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
// WouldBanForCrowdSec returns what BanForCrowdSec would, without making
|
||||
// the ban: what observe mode would have done.
|
||||
func (l *Ledger) WouldBanForCrowdSec(
|
||||
netblock netip.Prefix, now, expires time.Time, scenario string, notes Notes,
|
||||
netblock netip.Prefix,
|
||||
now, expires time.Time,
|
||||
scenario string,
|
||||
notes Notes,
|
||||
) (Ban, bool) {
|
||||
return l.ban(netblock, now, expires, CauseCrowdSec, crowdSecReason(scenario), notes,
|
||||
false)
|
||||
return l.ban(
|
||||
netblock,
|
||||
now,
|
||||
expires,
|
||||
CauseCrowdSec,
|
||||
crowdSecReason(scenario),
|
||||
notes,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
// WouldBePermanent reports whether a ban on netblock for cause, CauseLimit,
|
||||
@@ -505,7 +559,10 @@ func (l *Ledger) Bans(netblock netip.Prefix) []Ban {
|
||||
// about it. It is not a request from netblock, and leaves when it was last
|
||||
// seen unchanged. It does not have bans.json written at once: the notes
|
||||
// are written with its next write, as the counts in them are.
|
||||
func (l *Ledger) AddLookup(netblock netip.Prefix, asn, asName, country string) {
|
||||
func (l *Ledger) AddLookup(
|
||||
netblock netip.Prefix,
|
||||
asn, asName, country string,
|
||||
) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
@@ -654,7 +711,10 @@ func (l *Ledger) holds(netblock netip.Prefix, start time.Time) bool {
|
||||
// keep is true, the ban is not made, only returned: it is the ban that
|
||||
// would have been made.
|
||||
func (l *Ledger) ban(
|
||||
netblock netip.Prefix, now, expires time.Time, cause, reason string, notes Notes,
|
||||
netblock netip.Prefix,
|
||||
now, expires time.Time,
|
||||
cause, reason string,
|
||||
notes Notes,
|
||||
keep bool,
|
||||
) (Ban, bool) {
|
||||
l.mu.Lock()
|
||||
@@ -675,7 +735,13 @@ func (l *Ledger) ban(
|
||||
}
|
||||
|
||||
notes.Request = notes.Request.cut()
|
||||
ban := Ban{Netblock: netblock, Start: now, Cause: cause, Reason: reason, Notes: notes}
|
||||
ban := Ban{
|
||||
Netblock: netblock,
|
||||
Start: now,
|
||||
Cause: cause,
|
||||
Reason: reason,
|
||||
Notes: notes,
|
||||
}
|
||||
|
||||
switch cause {
|
||||
case CauseAttack:
|
||||
@@ -765,7 +831,9 @@ func (l *Ledger) active(client netip.Addr, now time.Time) *Ban {
|
||||
var first *Ban
|
||||
|
||||
for _, length := range lengths {
|
||||
bans, found := l.netblocks.Get(netip.PrefixFrom(client, length).Masked())
|
||||
bans, found := l.netblocks.Get(
|
||||
netip.PrefixFrom(client, length).Masked(),
|
||||
)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
@@ -798,7 +866,9 @@ func (l *Ledger) covering(client netip.Addr) []*[]Ban {
|
||||
var found []*[]Ban
|
||||
|
||||
for _, length := range lengths {
|
||||
bans, ok := l.netblocks.Peek(netip.PrefixFrom(client, length).Masked())
|
||||
bans, ok := l.netblocks.Peek(
|
||||
netip.PrefixFrom(client, length).Masked(),
|
||||
)
|
||||
if ok {
|
||||
found = append(found, bans)
|
||||
}
|
||||
@@ -852,13 +922,15 @@ func (l *Ledger) limitExpiry(held []Ban, now time.Time) time.Time {
|
||||
var last *Ban
|
||||
|
||||
for i, ban := range held {
|
||||
if (ban.Cause == CauseLimit || ban.Cause == CauseAdmin) && ban.Lifted.IsZero() &&
|
||||
if (ban.Cause == CauseLimit || ban.Cause == CauseAdmin) &&
|
||||
ban.Lifted.IsZero() &&
|
||||
(last == nil || ban.Expires.After(last.Expires)) {
|
||||
last = &held[i]
|
||||
}
|
||||
}
|
||||
|
||||
if last != nil && now.Sub(last.Expires) <= l.params.LimitBanRepeatWindow {
|
||||
if last != nil &&
|
||||
now.Sub(last.Expires) <= l.params.LimitBanRepeatWindow {
|
||||
lastLength := last.Expires.Sub(last.Start)
|
||||
// This is repeatFactor * lastLength > MaxBanDuration, written so
|
||||
// that it cannot overflow.
|
||||
|
||||
+212
-59
@@ -27,8 +27,14 @@ func TestRepeatsTripleUntilPermanent(t *testing.T) {
|
||||
length := time.Duration(hours) * time.Hour
|
||||
if !ban.Expires.Equal(now.Add(length)) ||
|
||||
ban.Notes.EarlierBans != (bans.EarlierBans{Limit: i}) {
|
||||
t.Fatalf("ban %d lasts %s with earlier bans %+v, want %d hours and %d for a limit",
|
||||
i+1, ban.Expires.Sub(now), ban.Notes.EarlierBans, hours, i)
|
||||
t.Fatalf(
|
||||
"ban %d lasts %s with earlier bans %+v, want %d hours and %d for a limit",
|
||||
i+1,
|
||||
ban.Expires.Sub(now),
|
||||
ban.Notes.EarlierBans,
|
||||
hours,
|
||||
i,
|
||||
)
|
||||
}
|
||||
|
||||
now = ban.Expires
|
||||
@@ -65,13 +71,27 @@ func TestRepeatWindowRunsOut(t *testing.T) {
|
||||
ledger := bans.New(defaultParams())
|
||||
netblock := netip.MustParsePrefix("203.0.113.9/32")
|
||||
|
||||
first, _ := ledger.BanForLimit(netblock, midnight(), bans.Notes{})
|
||||
second, _ := ledger.BanForLimit(netblock, first.Expires.Add(tc.gap), bans.Notes{})
|
||||
first, _ := ledger.BanForLimit(
|
||||
netblock,
|
||||
midnight(),
|
||||
bans.Notes{},
|
||||
)
|
||||
second, _ := ledger.BanForLimit(
|
||||
netblock,
|
||||
first.Expires.Add(tc.gap),
|
||||
bans.Notes{},
|
||||
)
|
||||
|
||||
if second.Expires.Sub(second.Start) != tc.want ||
|
||||
second.Notes.EarlierBans != (bans.EarlierBans{Limit: 1}) {
|
||||
t.Errorf("second ban lasts %s with earlier bans %+v, want %s and 1 for a limit",
|
||||
second.Expires.Sub(second.Start), second.Notes.EarlierBans, tc.want)
|
||||
t.Errorf(
|
||||
"second ban lasts %s with earlier bans %+v, want %s and 1 for a limit",
|
||||
second.Expires.Sub(
|
||||
second.Start,
|
||||
),
|
||||
second.Notes.EarlierBans,
|
||||
tc.want,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -84,8 +104,11 @@ func TestFirstBanLongerThanTheMaximumIsPermanent(t *testing.T) {
|
||||
params.LimitBanDuration = params.MaxBanDuration + time.Hour
|
||||
ledger := bans.New(params)
|
||||
|
||||
ban, _ := ledger.BanForLimit(netip.MustParsePrefix("203.0.113.9/32"), midnight(),
|
||||
bans.Notes{})
|
||||
ban, _ := ledger.BanForLimit(
|
||||
netip.MustParsePrefix("203.0.113.9/32"),
|
||||
midnight(),
|
||||
bans.Notes{},
|
||||
)
|
||||
if !ban.Permanent() {
|
||||
t.Errorf("first ban ends at %s, want a permanent one", ban.Expires)
|
||||
}
|
||||
@@ -106,7 +129,12 @@ func TestLongestBanSetFarOffDoesNotOverflow(t *testing.T) {
|
||||
for i := range 14 {
|
||||
ban, _ := ledger.BanForLimit(netblock, now, bans.Notes{})
|
||||
if !ban.Expires.After(ban.Start) {
|
||||
t.Fatalf("ban %d starts at %s and ends at %s", i+1, ban.Start, ban.Expires)
|
||||
t.Fatalf(
|
||||
"ban %d starts at %s and ends at %s",
|
||||
i+1,
|
||||
ban.Start,
|
||||
ban.Expires,
|
||||
)
|
||||
}
|
||||
|
||||
now = ban.Expires
|
||||
@@ -129,17 +157,36 @@ func TestBrokenLimitDuringABanMakesNoOther(t *testing.T) {
|
||||
t.Error("the first ban was not made")
|
||||
}
|
||||
|
||||
again, made := ledger.BanForLimit(netblock, midnight().Add(time.Minute), bans.Notes{})
|
||||
again, made := ledger.BanForLimit(
|
||||
netblock,
|
||||
midnight().Add(time.Minute),
|
||||
bans.Notes{},
|
||||
)
|
||||
|
||||
if made || !reflect.DeepEqual(again, first) || len(ledger.Bans(netblock)) != 1 {
|
||||
t.Errorf("a limit broken during a ban gave %+v, made %t, and %d bans, "+
|
||||
"want %+v, not made, and 1", again, made, len(ledger.Bans(netblock)), first)
|
||||
if made || !reflect.DeepEqual(again, first) ||
|
||||
len(ledger.Bans(netblock)) != 1 {
|
||||
t.Errorf(
|
||||
"a limit broken during a ban gave %+v, made %t, and %d bans, "+
|
||||
"want %+v, not made, and 1",
|
||||
again,
|
||||
made,
|
||||
len(ledger.Bans(netblock)),
|
||||
first,
|
||||
)
|
||||
}
|
||||
|
||||
again, made = ledger.BanForAttack(netblock, midnight().Add(time.Minute), bans.Notes{})
|
||||
again, made = ledger.BanForAttack(
|
||||
netblock,
|
||||
midnight().Add(time.Minute),
|
||||
bans.Notes{},
|
||||
)
|
||||
if made || !reflect.DeepEqual(again, first) {
|
||||
t.Errorf("an attack during a ban gave %+v, made %t, want %+v, not made",
|
||||
again, made, first)
|
||||
t.Errorf(
|
||||
"an attack during a ban gave %+v, made %t, want %+v, not made",
|
||||
again,
|
||||
made,
|
||||
first,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,16 +195,26 @@ func TestCheckRefusesWhileTheBanLastsAndCountsTheRefusals(t *testing.T) {
|
||||
|
||||
ledger := bans.New(defaultParams())
|
||||
netblock := netip.MustParsePrefix("203.0.113.9/32")
|
||||
ban, _ := ledger.BanForLimit(netblock, midnight(), bans.Notes{Requests: 5})
|
||||
ban, _ := ledger.BanForLimit(
|
||||
netblock,
|
||||
midnight(),
|
||||
bans.Notes{Requests: 5},
|
||||
)
|
||||
|
||||
for range 3 {
|
||||
got, banned, _ := ledger.Check(netblock.Addr(), ban.Expires.Add(-time.Nanosecond))
|
||||
got, banned, _ := ledger.Check(
|
||||
netblock.Addr(),
|
||||
ban.Expires.Add(-time.Nanosecond),
|
||||
)
|
||||
if !banned || got.Start != ban.Start {
|
||||
t.Fatalf("check during the ban gives %+v and %t", got, banned)
|
||||
}
|
||||
}
|
||||
|
||||
_, banned, _ := ledger.Check(netip.MustParseAddr("203.0.113.10"), midnight())
|
||||
_, banned, _ := ledger.Check(
|
||||
netip.MustParseAddr("203.0.113.10"),
|
||||
midnight(),
|
||||
)
|
||||
if banned {
|
||||
t.Error("another netblock is banned")
|
||||
}
|
||||
@@ -180,11 +237,23 @@ func TestFindCountsNothing(t *testing.T) {
|
||||
|
||||
ledger := bans.New(defaultParams())
|
||||
netblock := netip.MustParsePrefix("203.0.113.9/32")
|
||||
ban, _ := ledger.BanForLimit(netblock, midnight(), bans.Notes{Requests: 5})
|
||||
ban, _ := ledger.BanForLimit(
|
||||
netblock,
|
||||
midnight(),
|
||||
bans.Notes{Requests: 5},
|
||||
)
|
||||
|
||||
got, banned, _ := ledger.Find(netblock.Addr(), ban.Expires.Add(-time.Nanosecond))
|
||||
got, banned, _ := ledger.Find(
|
||||
netblock.Addr(),
|
||||
ban.Expires.Add(-time.Nanosecond),
|
||||
)
|
||||
if !banned || !reflect.DeepEqual(got, ban) {
|
||||
t.Errorf("find during the ban gives %+v and %t, want %+v", got, banned, ban)
|
||||
t.Errorf(
|
||||
"find during the ban gives %+v and %t, want %+v",
|
||||
got,
|
||||
banned,
|
||||
ban,
|
||||
)
|
||||
}
|
||||
|
||||
_, banned, _ = ledger.Find(netblock.Addr(), ban.Expires)
|
||||
@@ -192,12 +261,21 @@ func TestFindCountsNothing(t *testing.T) {
|
||||
t.Error("the ban did not end")
|
||||
}
|
||||
|
||||
if notes := ledger.Bans(netblock)[0].Notes; !reflect.DeepEqual(notes, ban.Notes) {
|
||||
t.Errorf("the notes are %+v, want them unchanged, %+v", notes, ban.Notes)
|
||||
if notes := ledger.Bans(netblock)[0].Notes; !reflect.DeepEqual(
|
||||
notes,
|
||||
ban.Notes,
|
||||
) {
|
||||
t.Errorf(
|
||||
"the notes are %+v, want them unchanged, %+v",
|
||||
notes,
|
||||
ban.Notes,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxBansDropsTheEarliestBanOfTheNetblockSeenLongestAgo(t *testing.T) {
|
||||
func TestMaxBansDropsTheEarliestBanOfTheNetblockSeenLongestAgo(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
params := defaultParams()
|
||||
@@ -230,7 +308,10 @@ func TestMaxBansDropsTheEarliestBanOfTheNetblockSeenLongestAgo(t *testing.T) {
|
||||
wantBans(t, ledger, map[netip.Prefix]int{a: 1, b: 1, d: 1})
|
||||
|
||||
if !ledger.Bans(a)[0].Start.Equal(first.Expires) {
|
||||
t.Errorf("a kept its ban of %s, want the later one", ledger.Bans(a)[0].Start)
|
||||
t.Errorf(
|
||||
"a kept its ban of %s, want the later one",
|
||||
ledger.Bans(a)[0].Start,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,41 +344,70 @@ func TestRequestDuringAnAttackBanMakesItPermanent(t *testing.T) {
|
||||
notes := bans.Notes{RuleID: "env-file", Target: "path"}
|
||||
|
||||
ban, _ := ledger.BanForAttack(netblock, midnight(), notes)
|
||||
if !ban.Expires.Equal(midnight().Add(7*day)) || ban.Cause != bans.CauseAttack ||
|
||||
ban.Notes.RuleID != "env-file" || ledger.Made(bans.CauseAttack) != 1 ||
|
||||
if !ban.Expires.Equal(midnight().Add(7*day)) ||
|
||||
ban.Cause != bans.CauseAttack ||
|
||||
ban.Notes.RuleID != "env-file" ||
|
||||
ledger.Made(bans.CauseAttack) != 1 ||
|
||||
ledger.Made(bans.CauseLimit) != 0 {
|
||||
t.Fatalf("the ban is %+v, with %d made for an attack and %d for a limit, "+
|
||||
"want one for an attack, of seven days", ban,
|
||||
ledger.Made(bans.CauseAttack), ledger.Made(bans.CauseLimit))
|
||||
t.Fatalf(
|
||||
"the ban is %+v, with %d made for an attack and %d for a limit, "+
|
||||
"want one for an attack, of seven days",
|
||||
ban,
|
||||
ledger.Made(bans.CauseAttack),
|
||||
ledger.Made(bans.CauseLimit),
|
||||
)
|
||||
}
|
||||
|
||||
wantChanged(t, ledger, true)
|
||||
|
||||
// In observe mode the ban refuses nothing, and stays as it is, while
|
||||
// Find tells that the request would have made it permanent.
|
||||
got, _, wouldMakePermanent := ledger.Find(netblock.Addr(), midnight().Add(time.Hour))
|
||||
if got.Permanent() || ledger.Bans(netblock)[0].Permanent() || !wouldMakePermanent {
|
||||
t.Fatalf("a request found under the ban left it %+v, would have made it "+
|
||||
"permanent %t, want it as it was, and true", got, wouldMakePermanent)
|
||||
got, _, wouldMakePermanent := ledger.Find(
|
||||
netblock.Addr(),
|
||||
midnight().Add(time.Hour),
|
||||
)
|
||||
if got.Permanent() || ledger.Bans(netblock)[0].Permanent() ||
|
||||
!wouldMakePermanent {
|
||||
t.Fatalf(
|
||||
"a request found under the ban left it %+v, would have made it "+
|
||||
"permanent %t, want it as it was, and true",
|
||||
got,
|
||||
wouldMakePermanent,
|
||||
)
|
||||
}
|
||||
|
||||
wantChanged(t, ledger, false)
|
||||
|
||||
// A request it refuses makes it permanent, says so, and makes
|
||||
// bans.json due.
|
||||
got, _, madePermanent := ledger.Check(netblock.Addr(), midnight().Add(time.Hour))
|
||||
if !madePermanent || !got.Permanent() || !ledger.Bans(netblock)[0].Permanent() {
|
||||
t.Fatalf("after a request during the ban, it is %+v, made permanent %t, "+
|
||||
"want it made permanent", got, madePermanent)
|
||||
got, _, madePermanent := ledger.Check(
|
||||
netblock.Addr(),
|
||||
midnight().Add(time.Hour),
|
||||
)
|
||||
if !madePermanent || !got.Permanent() ||
|
||||
!ledger.Bans(netblock)[0].Permanent() {
|
||||
t.Fatalf(
|
||||
"after a request during the ban, it is %+v, made permanent %t, "+
|
||||
"want it made permanent",
|
||||
got,
|
||||
madePermanent,
|
||||
)
|
||||
}
|
||||
|
||||
wantChanged(t, ledger, true)
|
||||
|
||||
// The next request finds it permanent already.
|
||||
_, banned, madePermanent := ledger.Check(netblock.Addr(), midnight().Add(100*365*day))
|
||||
_, banned, madePermanent := ledger.Check(
|
||||
netblock.Addr(),
|
||||
midnight().Add(100*365*day),
|
||||
)
|
||||
if !banned || madePermanent {
|
||||
t.Errorf("a later request is banned %t, and made the ban permanent %t, "+
|
||||
"want banned by the permanent ban", banned, madePermanent)
|
||||
t.Errorf(
|
||||
"a later request is banned %t, and made the ban permanent %t, "+
|
||||
"want banned by the permanent ban",
|
||||
banned,
|
||||
madePermanent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,7 +435,11 @@ func TestAttackAfterAnAttackBanHasEndedBansPermanently(t *testing.T) {
|
||||
|
||||
// Its notes show the earlier ban for an attack that makes it permanent,
|
||||
// beside the one for a limit.
|
||||
third, _ := ledger.BanForAttack(netblock, second.Expires.Add(30*day), bans.Notes{})
|
||||
third, _ := ledger.BanForAttack(
|
||||
netblock,
|
||||
second.Expires.Add(30*day),
|
||||
bans.Notes{},
|
||||
)
|
||||
if !third.Permanent() ||
|
||||
third.Notes.EarlierBans != (bans.EarlierBans{Limit: 1, Attack: 1}) {
|
||||
t.Errorf("the next ban for an attack is %+v, want a permanent one, "+
|
||||
@@ -342,10 +456,18 @@ func TestWouldBanGivesTheBanWithoutMakingIt(t *testing.T) {
|
||||
wantChanged(t, ledger, true)
|
||||
|
||||
// While the first ban lasts, none would be made.
|
||||
during, would := ledger.WouldBanForAttack(netblock, midnight(), bans.Notes{})
|
||||
during, would := ledger.WouldBanForAttack(
|
||||
netblock,
|
||||
midnight(),
|
||||
bans.Notes{},
|
||||
)
|
||||
if would || !reflect.DeepEqual(during, first) {
|
||||
t.Errorf("during the first ban, would ban %t with %+v, want false with %+v",
|
||||
would, during, first)
|
||||
t.Errorf(
|
||||
"during the first ban, would ban %t with %+v, want false with %+v",
|
||||
would,
|
||||
during,
|
||||
first,
|
||||
)
|
||||
}
|
||||
|
||||
// As it ends, a clear sign of attack would ban for seven days, and a
|
||||
@@ -353,19 +475,31 @@ func TestWouldBanGivesTheBanWithoutMakingIt(t *testing.T) {
|
||||
limitNotes := bans.Notes{Kind: "requests", Limit: 1, Window: "minute"}
|
||||
attack, wouldAttack := ledger.WouldBanForAttack(netblock, first.Expires,
|
||||
bans.Notes{RuleID: "git-dir"})
|
||||
limit, wouldLimit := ledger.WouldBanForLimit(netblock, first.Expires, limitNotes)
|
||||
limit, wouldLimit := ledger.WouldBanForLimit(
|
||||
netblock,
|
||||
first.Expires,
|
||||
limitNotes,
|
||||
)
|
||||
|
||||
if !wouldAttack || !attack.Expires.Equal(first.Expires.Add(7*day)) ||
|
||||
attack.Reason != "matched the rule git-dir" || !wouldLimit ||
|
||||
!limit.Expires.Equal(first.Expires.Add(3*time.Hour)) ||
|
||||
limit.Reason != "requests per minute over the limit of 1" {
|
||||
t.Errorf("would ban with %+v and %+v, want seven days for the attack and "+
|
||||
"three hours for the limit", attack, limit)
|
||||
t.Errorf(
|
||||
"would ban with %+v and %+v, want seven days for the attack and "+
|
||||
"three hours for the limit",
|
||||
attack,
|
||||
limit,
|
||||
)
|
||||
}
|
||||
|
||||
if len(ledger.Bans(netblock)) != 1 || ledger.Made(bans.CauseLimit) != 1 ||
|
||||
if len(ledger.Bans(netblock)) != 1 ||
|
||||
ledger.Made(bans.CauseLimit) != 1 ||
|
||||
ledger.Made(bans.CauseAttack) != 0 {
|
||||
t.Errorf("the ledger holds %+v, want the first ban alone", ledger.Bans(netblock))
|
||||
t.Errorf(
|
||||
"the ledger holds %+v, want the first ban alone",
|
||||
ledger.Bans(netblock),
|
||||
)
|
||||
}
|
||||
|
||||
wantChanged(t, ledger, false)
|
||||
@@ -422,7 +556,8 @@ func TestAttackBanDoesNotLengthenTheNextBanForALimit(t *testing.T) {
|
||||
attack, _ := ledger.BanForAttack(netblock, midnight(), bans.Notes{})
|
||||
limit, _ := ledger.BanForLimit(netblock, attack.Expires, bans.Notes{})
|
||||
|
||||
if limit.Expires.Sub(limit.Start) != time.Hour || limit.Cause != bans.CauseLimit {
|
||||
if limit.Expires.Sub(limit.Start) != time.Hour ||
|
||||
limit.Cause != bans.CauseLimit {
|
||||
t.Errorf("the ban for a limit is %+v, want one of an hour", limit)
|
||||
}
|
||||
|
||||
@@ -443,15 +578,23 @@ func TestRequestTextsAreCutTo256Bytes(t *testing.T) {
|
||||
Time: midnight(), Method: long, Host: long, Path: long, Status: 403, UserAgent: long,
|
||||
}
|
||||
|
||||
ban, _ := ledger.BanForLimit(netblock, midnight(), bans.Notes{Request: request})
|
||||
ban, _ := ledger.BanForLimit(
|
||||
netblock,
|
||||
midnight(),
|
||||
bans.Notes{Request: request},
|
||||
)
|
||||
|
||||
cut := long[:256]
|
||||
want := bans.Request{
|
||||
Time: midnight(), Method: cut, Host: cut, Path: cut, Status: 403, UserAgent: cut,
|
||||
}
|
||||
|
||||
if ban.Notes.Request != want || ledger.Bans(netblock)[0].Notes.Request != want {
|
||||
t.Errorf("the notes keep %+v, want each text cut to 256 bytes", ban.Notes.Request)
|
||||
if ban.Notes.Request != want ||
|
||||
ledger.Bans(netblock)[0].Notes.Request != want {
|
||||
t.Errorf(
|
||||
"the notes keep %+v, want each text cut to 256 bytes",
|
||||
ban.Notes.Request,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,13 +625,23 @@ func TestLookupFillsTheNotesOfTheNetblocksBansWithoutOne(t *testing.T) {
|
||||
{ASN: "AS64496", ASName: "Example Net", Country: "DE"},
|
||||
} {
|
||||
got := held[i].Notes
|
||||
if got.ASN != want.ASN || got.ASName != want.ASName || got.Country != want.Country {
|
||||
t.Errorf("ban %d's notes give %q, %q and %q, want %q, %q and %q", i+1,
|
||||
got.ASN, got.ASName, got.Country, want.ASN, want.ASName, want.Country)
|
||||
if got.ASN != want.ASN || got.ASName != want.ASName ||
|
||||
got.Country != want.Country {
|
||||
t.Errorf(
|
||||
"ban %d's notes give %q, %q and %q, want %q, %q and %q",
|
||||
i+1,
|
||||
got.ASN,
|
||||
got.ASName,
|
||||
got.Country,
|
||||
want.ASN,
|
||||
want.ASName,
|
||||
want.Country,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if notes := ledger.Bans(other)[0].Notes; notes.ASN != "" || notes.Country != "" {
|
||||
if notes := ledger.Bans(other)[0].Notes; notes.ASN != "" ||
|
||||
notes.Country != "" {
|
||||
t.Errorf("the ban on %s has %q and %q, want neither",
|
||||
other, notes.ASN, notes.Country)
|
||||
}
|
||||
|
||||
@@ -20,29 +20,55 @@ func TestCrowdSecBanLastsUntilTheDecisionEnds(t *testing.T) {
|
||||
expires := midnight().Add(4 * time.Hour)
|
||||
|
||||
// The ban that would be made is not made.
|
||||
would, wouldBan := ledger.WouldBanForCrowdSec(netblock, midnight(), expires,
|
||||
scenario, bans.Notes{})
|
||||
would, wouldBan := ledger.WouldBanForCrowdSec(
|
||||
netblock,
|
||||
midnight(),
|
||||
expires,
|
||||
scenario,
|
||||
bans.Notes{},
|
||||
)
|
||||
if !wouldBan || len(ledger.Bans(netblock)) != 0 {
|
||||
t.Errorf("would ban %t, and the ledger holds %+v, want true and nothing",
|
||||
wouldBan, ledger.Bans(netblock))
|
||||
t.Errorf(
|
||||
"would ban %t, and the ledger holds %+v, want true and nothing",
|
||||
wouldBan,
|
||||
ledger.Bans(netblock),
|
||||
)
|
||||
}
|
||||
|
||||
const reason = "CrowdSec's decision for " + scenario
|
||||
|
||||
ban, made := ledger.BanForCrowdSec(netblock, midnight(), expires, scenario,
|
||||
bans.Notes{})
|
||||
if !made || !reflect.DeepEqual(ban, would) || ban.Cause != bans.CauseCrowdSec ||
|
||||
!ban.Expires.Equal(expires) || ban.Reason != reason ||
|
||||
ban, made := ledger.BanForCrowdSec(
|
||||
netblock,
|
||||
midnight(),
|
||||
expires,
|
||||
scenario,
|
||||
bans.Notes{},
|
||||
)
|
||||
if !made || !reflect.DeepEqual(ban, would) ||
|
||||
ban.Cause != bans.CauseCrowdSec ||
|
||||
!ban.Expires.Equal(expires) ||
|
||||
ban.Reason != reason ||
|
||||
ledger.Made(bans.CauseCrowdSec) != 1 {
|
||||
t.Errorf("made %t the ban %+v, want the one that would be made, %+v, for "+
|
||||
"crowdsec until %s", made, ban, would, expires)
|
||||
t.Errorf(
|
||||
"made %t the ban %+v, want the one that would be made, %+v, for "+
|
||||
"crowdsec until %s",
|
||||
made,
|
||||
ban,
|
||||
would,
|
||||
expires,
|
||||
)
|
||||
}
|
||||
|
||||
// A second decision on the netblock while the ban lasts makes no other.
|
||||
again, made := ledger.BanForCrowdSec(netblock, midnight().Add(time.Hour),
|
||||
expires.Add(time.Hour), scenario, bans.Notes{})
|
||||
if made || !again.Expires.Equal(expires) || ledger.Made(bans.CauseCrowdSec) != 1 {
|
||||
t.Errorf("made %t the ban %+v while the first lasts, want none", made, again)
|
||||
if made || !again.Expires.Equal(expires) ||
|
||||
ledger.Made(bans.CauseCrowdSec) != 1 {
|
||||
t.Errorf(
|
||||
"made %t the ban %+v while the first lasts, want none",
|
||||
made,
|
||||
again,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +78,13 @@ func TestCrowdSecBanIsNeverMadePermanent(t *testing.T) {
|
||||
ledger := bans.New(defaultParams())
|
||||
netblock := netip.MustParsePrefix("203.0.113.9/32")
|
||||
expires := midnight().Add(4 * time.Hour)
|
||||
ledger.BanForCrowdSec(netblock, midnight(), expires, scenario, bans.Notes{})
|
||||
ledger.BanForCrowdSec(
|
||||
netblock,
|
||||
midnight(),
|
||||
expires,
|
||||
scenario,
|
||||
bans.Notes{},
|
||||
)
|
||||
|
||||
// A request as the ban ends is refused, and leaves it as it is.
|
||||
last := expires.Add(-time.Nanosecond)
|
||||
@@ -60,8 +92,13 @@ func TestCrowdSecBanIsNeverMadePermanent(t *testing.T) {
|
||||
held, banned, madePermanent := ledger.Check(netblock.Addr(), last)
|
||||
if !banned || madePermanent || !held.Expires.Equal(expires) ||
|
||||
ledger.WouldBePermanent(netblock, last, bans.CauseCrowdSec) {
|
||||
t.Errorf("as the ban ends, banned %t with %+v, made permanent %t, want "+
|
||||
"refused under the ban as it was", banned, held, madePermanent)
|
||||
t.Errorf(
|
||||
"as the ban ends, banned %t with %+v, made permanent %t, want "+
|
||||
"refused under the ban as it was",
|
||||
banned,
|
||||
held,
|
||||
madePermanent,
|
||||
)
|
||||
}
|
||||
|
||||
if _, banned, _ := ledger.Check(netblock.Addr(), expires); banned {
|
||||
@@ -69,7 +106,9 @@ func TestCrowdSecBanIsNeverMadePermanent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrowdSecBanIsCountedAndDoesNotLengthenTheNextBanForALimit(t *testing.T) {
|
||||
func TestCrowdSecBanIsCountedAndDoesNotLengthenTheNextBanForALimit(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
ledger := bans.New(defaultParams())
|
||||
@@ -78,13 +117,20 @@ func TestCrowdSecBanIsCountedAndDoesNotLengthenTheNextBanForALimit(t *testing.T)
|
||||
// Three times the three days would be permanent; a limit broken as the
|
||||
// ban for CrowdSec's decision ends bans for an hour, as a first broken
|
||||
// limit does.
|
||||
crowdSec, _ := ledger.BanForCrowdSec(netblock, midnight(), midnight().Add(3*day),
|
||||
scenario, bans.Notes{})
|
||||
crowdSec, _ := ledger.BanForCrowdSec(
|
||||
netblock,
|
||||
midnight(),
|
||||
midnight().Add(3*day),
|
||||
scenario,
|
||||
bans.Notes{},
|
||||
)
|
||||
limit, _ := ledger.BanForLimit(netblock, crowdSec.Expires, bans.Notes{})
|
||||
|
||||
if limit.Expires.Sub(limit.Start) != time.Hour ||
|
||||
limit.Notes.EarlierBans != (bans.EarlierBans{CrowdSec: 1}) {
|
||||
t.Errorf("the ban for a limit is %+v, want one of an hour after one for crowdsec",
|
||||
limit)
|
||||
t.Errorf(
|
||||
"the ban for a limit is %+v, want one of an hour after one for crowdsec",
|
||||
limit,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,16 @@ func TestChangedAfterABanIsMade(t *testing.T) {
|
||||
wantChanged(t, ledger, false)
|
||||
|
||||
// Two bans before the value is read leave one.
|
||||
ledger.BanForLimit(netip.MustParsePrefix("203.0.113.10/32"), midnight(), bans.Notes{})
|
||||
ledger.BanForLimit(netip.MustParsePrefix("203.0.113.11/32"), midnight(), bans.Notes{})
|
||||
ledger.BanForLimit(
|
||||
netip.MustParsePrefix("203.0.113.10/32"),
|
||||
midnight(),
|
||||
bans.Notes{},
|
||||
)
|
||||
ledger.BanForLimit(
|
||||
netip.MustParsePrefix("203.0.113.11/32"),
|
||||
midnight(),
|
||||
bans.Notes{},
|
||||
)
|
||||
wantChanged(t, ledger, true)
|
||||
wantChanged(t, ledger, false)
|
||||
}
|
||||
@@ -52,7 +60,10 @@ func TestSnapshotListsEveryBanByNetblock(t *testing.T) {
|
||||
|
||||
got := make([]string, 0, len(snapshot))
|
||||
for _, ban := range snapshot {
|
||||
got = append(got, ban.Netblock.String()+" "+ban.Start.Format(time.Kitchen))
|
||||
got = append(
|
||||
got,
|
||||
ban.Netblock.String()+" "+ban.Start.Format(time.Kitchen),
|
||||
)
|
||||
}
|
||||
|
||||
want := []string{
|
||||
@@ -77,7 +88,10 @@ func TestLoadedBansCarryOn(t *testing.T) {
|
||||
after := bans.New(defaultParams())
|
||||
after.Load(before.Snapshot())
|
||||
|
||||
_, banned, _ := after.Check(netblock.Addr(), ban.Expires.Add(-time.Second))
|
||||
_, banned, _ := after.Check(
|
||||
netblock.Addr(),
|
||||
ban.Expires.Add(-time.Second),
|
||||
)
|
||||
if !banned {
|
||||
t.Error("the loaded ban does not refuse")
|
||||
}
|
||||
@@ -85,8 +99,11 @@ func TestLoadedBansCarryOn(t *testing.T) {
|
||||
again, _ := after.BanForLimit(netblock, ban.Expires, bans.Notes{})
|
||||
if again.Expires.Sub(again.Start) != 3*time.Hour ||
|
||||
again.Notes.EarlierBans != (bans.EarlierBans{Limit: 1}) {
|
||||
t.Errorf("the next ban lasts %s with earlier bans %+v, want 3h and 1 for a limit",
|
||||
again.Expires.Sub(again.Start), again.Notes.EarlierBans)
|
||||
t.Errorf(
|
||||
"the next ban lasts %s with earlier bans %+v, want 3h and 1 for a limit",
|
||||
again.Expires.Sub(again.Start),
|
||||
again.Notes.EarlierBans,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,10 +115,20 @@ func TestLoadedBanRefusesEveryClientInItsNetblock(t *testing.T) {
|
||||
// ban covers, beside a ban the ledger makes on one IPv4 address.
|
||||
ledger := bans.New(defaultParams())
|
||||
ledger.Load([]bans.Ban{
|
||||
{Netblock: netip.MustParsePrefix("203.0.113.9/24"), Start: midnight()},
|
||||
{Netblock: netip.MustParsePrefix("2001:db8::1/48"), Start: midnight()},
|
||||
{
|
||||
Netblock: netip.MustParsePrefix("203.0.113.9/24"),
|
||||
Start: midnight(),
|
||||
},
|
||||
{
|
||||
Netblock: netip.MustParsePrefix("2001:db8::1/48"),
|
||||
Start: midnight(),
|
||||
},
|
||||
})
|
||||
ledger.BanForLimit(netip.MustParsePrefix("198.51.100.7/32"), midnight(), bans.Notes{})
|
||||
ledger.BanForLimit(
|
||||
netip.MustParsePrefix("198.51.100.7/32"),
|
||||
midnight(),
|
||||
bans.Notes{},
|
||||
)
|
||||
|
||||
for client, want := range map[string]bool{
|
||||
"203.0.113.0": true,
|
||||
@@ -138,7 +165,10 @@ func TestPermanentBanStartedBeforeAnEndedOneRefuses(t *testing.T) {
|
||||
// As when an admin adds a permanent ban to bans.json with a start
|
||||
// before that of the netblock's ban that has ended.
|
||||
netblock := netip.MustParsePrefix("203.0.113.0/24")
|
||||
permanent := bans.Ban{Netblock: netblock, Start: midnight().Add(-time.Hour)}
|
||||
permanent := bans.Ban{
|
||||
Netblock: netblock,
|
||||
Start: midnight().Add(-time.Hour),
|
||||
}
|
||||
ended := bans.Ban{
|
||||
Netblock: netblock,
|
||||
Start: midnight(),
|
||||
@@ -153,20 +183,31 @@ func TestPermanentBanStartedBeforeAnEndedOneRefuses(t *testing.T) {
|
||||
|
||||
ban, banned, _ := ledger.Find(client, now)
|
||||
if !banned || !ban.Permanent() {
|
||||
t.Errorf("find gives %+v and %t, want the permanent ban", ban, banned)
|
||||
t.Errorf(
|
||||
"find gives %+v and %t, want the permanent ban",
|
||||
ban,
|
||||
banned,
|
||||
)
|
||||
}
|
||||
|
||||
ban, banned, _ = ledger.Check(client, now)
|
||||
if !banned || !ban.Permanent() {
|
||||
t.Errorf("the client is refused: %t, under %+v, want under the permanent ban",
|
||||
banned, ban)
|
||||
t.Errorf(
|
||||
"the client is refused: %t, under %+v, want under the permanent ban",
|
||||
banned,
|
||||
ban,
|
||||
)
|
||||
}
|
||||
|
||||
// A limit broken now makes no shorter ban over the permanent one.
|
||||
ban, _ = ledger.BanForLimit(netblock, now, bans.Notes{})
|
||||
if !ban.Permanent() || len(ledger.Bans(netblock)) != 2 {
|
||||
t.Errorf("a broken limit returned %+v and left the netblock %d bans, "+
|
||||
"want the permanent ban and 2", ban, len(ledger.Bans(netblock)))
|
||||
t.Errorf(
|
||||
"a broken limit returned %+v and left the netblock %d bans, "+
|
||||
"want the permanent ban and 2",
|
||||
ban,
|
||||
len(ledger.Bans(netblock)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +236,11 @@ func TestNextBanWorkedOutFromTheBanThatEndedLast(t *testing.T) {
|
||||
// Once both have ended, a limit broken within the repeat window bans
|
||||
// for three times the 9 hours, and the notes count the two bans
|
||||
// before the 9-hour one and it, for a limit, and the admin's.
|
||||
ban, _ := ledger.BanForLimit(netblock, nineHours.Expires.Add(time.Hour), bans.Notes{})
|
||||
ban, _ := ledger.BanForLimit(
|
||||
netblock,
|
||||
nineHours.Expires.Add(time.Hour),
|
||||
bans.Notes{},
|
||||
)
|
||||
if ban.Expires.Sub(ban.Start) != 27*time.Hour ||
|
||||
ban.Notes.EarlierBans != (bans.EarlierBans{Limit: 3, Admin: 1}) {
|
||||
t.Errorf("the next ban lasts %s with earlier bans %+v, "+
|
||||
@@ -226,7 +271,10 @@ func TestLoadKeepsAtMostMaxBansDroppingTheEarliest(t *testing.T) {
|
||||
|
||||
held := ledger.Snapshot()
|
||||
if len(held) != 1 || !reflect.DeepEqual(held[0], later) {
|
||||
t.Errorf("the ledger holds %+v, want only the ban that began later", held)
|
||||
t.Errorf(
|
||||
"the ledger holds %+v, want only the ban that began later",
|
||||
held,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,16 +304,25 @@ func TestLoadReplacesTheBansHeld(t *testing.T) {
|
||||
// bans.json is taken in, that ban is lifted.
|
||||
ledger.Load([]bans.Ban{kept})
|
||||
|
||||
_, banned, _ := ledger.Check(netip.MustParseAddr("203.0.113.9"), midnight())
|
||||
_, banned, _ := ledger.Check(
|
||||
netip.MustParseAddr("203.0.113.9"),
|
||||
midnight(),
|
||||
)
|
||||
if banned {
|
||||
t.Error("a ban left out of the second load still refuses")
|
||||
}
|
||||
|
||||
// The ledger holds one ban, so it makes two more without dropping any.
|
||||
first, _ := ledger.BanForLimit(netip.MustParsePrefix("198.51.100.7/32"), midnight(),
|
||||
bans.Notes{})
|
||||
second, _ := ledger.BanForLimit(netip.MustParsePrefix("198.51.100.8/32"), midnight(),
|
||||
bans.Notes{})
|
||||
first, _ := ledger.BanForLimit(
|
||||
netip.MustParsePrefix("198.51.100.7/32"),
|
||||
midnight(),
|
||||
bans.Notes{},
|
||||
)
|
||||
second, _ := ledger.BanForLimit(
|
||||
netip.MustParsePrefix("198.51.100.8/32"),
|
||||
midnight(),
|
||||
bans.Notes{},
|
||||
)
|
||||
|
||||
want := []bans.Ban{first, second, kept}
|
||||
if got := ledger.Snapshot(); !reflect.DeepEqual(got, want) {
|
||||
|
||||
Reference in new issue
Block a user