Add Certificates page and Traefik API frontend client
This commit is contained in:
parent
9e4c612dcb
commit
7fc90524b5
40 changed files with 6585 additions and 359 deletions
70
backend/internal/auth/ratelimit.go
Normal file
70
backend/internal/auth/ratelimit.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LoginRateLimiter tracks failed login attempts per key (IP or username) and enforces backoff.
|
||||
type LoginRateLimiter struct {
|
||||
mu sync.Mutex
|
||||
attempts map[string][]time.Time
|
||||
// config
|
||||
maxAttempts int
|
||||
window time.Duration
|
||||
blockDuration time.Duration
|
||||
}
|
||||
|
||||
func NewLoginRateLimiter(maxAttempts int, window time.Duration, blockDuration time.Duration) *LoginRateLimiter {
|
||||
return &LoginRateLimiter{
|
||||
attempts: make(map[string][]time.Time),
|
||||
maxAttempts: maxAttempts,
|
||||
window: window,
|
||||
blockDuration: blockDuration,
|
||||
}
|
||||
}
|
||||
|
||||
// Allow returns true if the key is allowed to attempt login now.
|
||||
// It also cleans up old entries.
|
||||
func (r *LoginRateLimiter) Allow(key string) bool {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
now := time.Now()
|
||||
times := r.attempts[key]
|
||||
// filter within window
|
||||
var filtered []time.Time
|
||||
for _, t := range times {
|
||||
if now.Sub(t) < r.window {
|
||||
filtered = append(filtered, t)
|
||||
}
|
||||
}
|
||||
r.attempts[key] = filtered
|
||||
if len(filtered) >= r.maxAttempts {
|
||||
// Check if still within block duration from last attempt
|
||||
last := filtered[len(filtered)-1]
|
||||
if now.Sub(last) < r.blockDuration {
|
||||
return false
|
||||
}
|
||||
// block expired, allow and reset
|
||||
r.attempts[key] = nil
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// RecordFailure records a failed attempt for key.
|
||||
func (r *LoginRateLimiter) RecordFailure(key string) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.attempts[key] = append(r.attempts[key], time.Now())
|
||||
}
|
||||
|
||||
// RecordSuccess clears failures for key.
|
||||
func (r *LoginRateLimiter) RecordSuccess(key string) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
delete(r.attempts, key)
|
||||
}
|
||||
|
||||
// DefaultLoginLimiter is a global limiter: 5 failures per minute, block for 30s after
|
||||
var DefaultLoginLimiter = NewLoginRateLimiter(5, time.Minute, 30*time.Second)
|
||||
Loading…
Add table
Add a link
Reference in a new issue