plugin/antibrute: new "block IP after" option #578
Massimo Melina committed
May 5, 2024 at 22:50 UTC
c171649ad31d582027a803615f925a47c86c4c4e
1 file changed
+12
-4
plugins/antibrute/plugin.js
+12
-4
@@ -3,8 +3,10 @@ exports.description = "Introduce increasing delays between login attempts."
3
exports.apiRequired = 8.8 // attemptingLogin
4
5
exports.config = {
6
- increment: { type: 'number', min: 1, defaultValue: 5, helperText: "Seconds to add to the delay for each login attempt" },
7
- max: { type: 'number', min: 1, defaultValue: 60, helperText: "Max seconds to delay before next login is allowed" },
6
+ increment: { type: 'number', min: 1, defaultValue: 5, unit: "seconds", helperText: "How longer user must wait for each login attempt" },
7
+ max: { type: 'number', min: 1, defaultValue: 60, label: "Max delay", unit: "seconds", helperText: "Max seconds to delay before next login is allowed" },
8
+ blockAfter: { type: 'number', xs: 6, min: 1, max: 9999, defaultValue: 100, label: "Block IP after", unit: "attempts", helperText: "localhost excluded" },
9
+ blockForHours: { type: 'number', xs: 6, min: 0, defaultValue: 24, label: "Block for", unit: "hours" },
10
}
11
exports.configDialog = {
12
maxWidth: 'xs',
@@ -13,7 +15,8 @@ exports.configDialog = {
15
const byIp = {}
16
17
exports.init = api => {
16
- const { getOrSet } = api.require('./misc')
18
+ const { getOrSet, isLocalHost, HOUR } = api.require('./misc')
19
+ const { block } = api.require('./block')
20
return {
21
unload: api.events.multi({
22
attemptingLogin: async ctx => {
@@ -24,13 +27,18 @@ exports.init = api => {
27
const delay = Math.min(max, 1000 * api.getConfig('increment') * ++rec.attempts)
28
const wait = rec.next - now
29
rec.next = new Date(+rec.next + delay)
30
+ if (rec.attempts > api.getConfig('blockAfter') && !isLocalHost(ctx)) {
31
+ const hours = api.getConfig('blockForHours')
32
+ const newRule = { ip, comment: "From antibrute plugin", expire: hours ? new Date(now.getTime() + hours * HOUR) : undefined }
33
+ block.set(x => [...x, newRule])
34
+ }
35
clearTimeout(rec.timer)
36
if (wait > 0) {
37
api.log('delaying', ip, 'for', Math.round(wait / 1000))
38
ctx.set('x-anti-brute-force', wait)
39
await new Promise(resolve => setTimeout(resolve, wait))
40
}
33
- rec.timer = setTimeout(() => delete byIp[ip], max * 10) // no memory leak
41
+ rec.timer = setTimeout(() => delete byIp[ip], 24 * HOUR) // no memory leak
42
},
43
login: ctx => {
44
if (ctx.state.account)