4fd7fad2e5
Updates #2280.
Squashed commit of the following:
commit d8c6aacb664361a13dde8522de2470dd137bed00
Merge: 84df492b 12f1e4ed
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Jun 15 17:21:41 2021 +0300
Merge branch 'master' into 2280-dns-timeout
commit 84df492b0134e88e031f586333437f503b90b7ae
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Jun 15 16:49:41 2021 +0300
home: fix docs & naming
commit af44a86a60ea815ca7100edc34db8acbdcc2cccf
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Jun 15 15:55:12 2021 +0300
all: imp docs & tests
commit 6ed6599fa0024cc7d14dc7c75ddda62e5179fe00
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Jun 15 15:26:22 2021 +0300
home: imp duration tests
commit 8fe7cb099dccfce3f9329d7207ef48f488f07e83
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Jun 15 15:04:16 2021 +0300
all: imp code, docs & tests
commit a989e8a5a6acede0063141cdbfc103b150b33d97
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Sat Jun 12 19:02:23 2021 +0300
WIP
commit b0362e22040a1d38f81dcc775c5ef6f7d1e94eee
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Sat Jun 12 18:58:09 2021 +0300
all: imp docs & tests
commit 64b00fd0854f3ddcb0189f3c93f3ffa2a31a98be
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Sat Jun 12 03:44:29 2021 +0300
home: introduce marshalable duration
commit bfb1a5706c37fcd27bccce4a5aec37dca3cf238b
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Sat Jun 12 01:56:10 2021 +0300
all: add upstream timeout setting
110 lines
2.6 KiB
Go
110 lines
2.6 KiB
Go
package home
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// failedAuthTTL is the period of time for which the failed attempt will stay in
|
|
// cache.
|
|
const failedAuthTTL = 1 * time.Minute
|
|
|
|
// failedAuth is an entry of authRateLimiter's cache.
|
|
type failedAuth struct {
|
|
until time.Time
|
|
num uint
|
|
}
|
|
|
|
// authRateLimiter used to cache failed authentication attempts.
|
|
type authRateLimiter struct {
|
|
failedAuths map[string]failedAuth
|
|
// failedAuthsLock protects failedAuths.
|
|
failedAuthsLock sync.Mutex
|
|
blockDur time.Duration
|
|
maxAttempts uint
|
|
}
|
|
|
|
// newAuthRateLimiter returns properly initialized *authRateLimiter.
|
|
func newAuthRateLimiter(blockDur time.Duration, maxAttempts uint) (ab *authRateLimiter) {
|
|
return &authRateLimiter{
|
|
failedAuths: make(map[string]failedAuth),
|
|
blockDur: blockDur,
|
|
maxAttempts: maxAttempts,
|
|
}
|
|
}
|
|
|
|
// cleanupLocked checks each blocked users removing ones with expired TTL. For
|
|
// internal use only.
|
|
func (ab *authRateLimiter) cleanupLocked(now time.Time) {
|
|
for k, v := range ab.failedAuths {
|
|
if now.After(v.until) {
|
|
delete(ab.failedAuths, k)
|
|
}
|
|
}
|
|
}
|
|
|
|
// checkLocked checks the attempter for it's state. For internal use only.
|
|
func (ab *authRateLimiter) checkLocked(usrID string, now time.Time) (left time.Duration) {
|
|
a, ok := ab.failedAuths[usrID]
|
|
if !ok {
|
|
return 0
|
|
}
|
|
|
|
if a.num < ab.maxAttempts {
|
|
return 0
|
|
}
|
|
|
|
return a.until.Sub(now)
|
|
}
|
|
|
|
// check returns the time left until unblocking. The nonpositive result should
|
|
// be interpreted as not blocked attempter.
|
|
func (ab *authRateLimiter) check(usrID string) (left time.Duration) {
|
|
now := time.Now()
|
|
|
|
ab.failedAuthsLock.Lock()
|
|
defer ab.failedAuthsLock.Unlock()
|
|
|
|
ab.cleanupLocked(now)
|
|
return ab.checkLocked(usrID, now)
|
|
}
|
|
|
|
// incLocked increments the number of unsuccessful attempts for attempter with
|
|
// ip and updates it's blocking moment if needed. For internal use only.
|
|
func (ab *authRateLimiter) incLocked(usrID string, now time.Time) {
|
|
until := now.Add(failedAuthTTL)
|
|
var attNum uint = 1
|
|
|
|
a, ok := ab.failedAuths[usrID]
|
|
if ok {
|
|
until = a.until
|
|
attNum = a.num + 1
|
|
}
|
|
if attNum >= ab.maxAttempts {
|
|
until = now.Add(ab.blockDur)
|
|
}
|
|
|
|
ab.failedAuths[usrID] = failedAuth{
|
|
num: attNum,
|
|
until: until,
|
|
}
|
|
}
|
|
|
|
// inc updates the failed attempt in cache.
|
|
func (ab *authRateLimiter) inc(usrID string) {
|
|
now := time.Now()
|
|
|
|
ab.failedAuthsLock.Lock()
|
|
defer ab.failedAuthsLock.Unlock()
|
|
|
|
ab.incLocked(usrID, now)
|
|
}
|
|
|
|
// remove stops any tracking and any blocking of the user.
|
|
func (ab *authRateLimiter) remove(usrID string) {
|
|
ab.failedAuthsLock.Lock()
|
|
defer ab.failedAuthsLock.Unlock()
|
|
|
|
delete(ab.failedAuths, usrID)
|
|
}
|