Documentation
¶
Overview ¶
Package lockout provides the login account-lockout limiter and the admin-API rate limiter. It is a self-contained leaf (stdlib only, no Culvert coupling) extracted from the flat package main per ADR-0002.
Index ¶
- Constants
- func Msg(seconds int) string
- type APIRateLimiter
- type LoginLimiter
- func (l *LoginLimiter) AttemptsLeft(username string) int
- func (l *LoginLimiter) Check(username string) (locked bool, secondsRemaining int)
- func (l *LoginLimiter) Cleanup()
- func (l *LoginLimiter) RecordFailure(username string) bool
- func (l *LoginLimiter) RecordSuccess(username string)
- func (l *LoginLimiter) SnapshotAndClear() func()
Constants ¶
const ( // MaxAttempts is the number of consecutive failures that triggers a lock. MaxAttempts = 5 // Window is the span within which failures accumulate toward a lock. Window = 10 * time.Minute // Duration is how long an account stays locked once tripped. Duration = 15 * time.Minute )
const ( // Burst is the max API mutations allowed per window. Burst = 60 // RateWindow is the sliding window width for the API rate limiter. RateWindow = 1 * time.Minute )
Variables ¶
This section is empty.
Functions ¶
Types ¶
type APIRateLimiter ¶
type APIRateLimiter struct {
// contains filtered or unexported fields
}
APIRateLimiter limits mutating admin API calls per client IP.
func NewAPIRateLimiter ¶
func NewAPIRateLimiter() *APIRateLimiter
NewAPIRateLimiter returns a ready-to-use APIRateLimiter.
func (*APIRateLimiter) Allow ¶
func (a *APIRateLimiter) Allow(ip string) bool
Allow returns true if the IP is within the rate limit for API mutations.
func (*APIRateLimiter) Cleanup ¶
func (a *APIRateLimiter) Cleanup()
Cleanup removes expired entries.
type LoginLimiter ¶
type LoginLimiter struct {
// contains filtered or unexported fields
}
LoginLimiter tracks failed login attempts per username.
func NewLoginLimiter ¶
func NewLoginLimiter() *LoginLimiter
NewLoginLimiter returns a ready-to-use LoginLimiter.
func (*LoginLimiter) AttemptsLeft ¶
func (l *LoginLimiter) AttemptsLeft(username string) int
AttemptsLeft returns how many more failures are allowed before lockout.
func (*LoginLimiter) Check ¶
func (l *LoginLimiter) Check(username string) (locked bool, secondsRemaining int)
Check returns (locked bool, secondsRemaining int). A locked account must not be verified further.
func (*LoginLimiter) Cleanup ¶ added in v1.0.22
func (l *LoginLimiter) Cleanup()
Cleanup removes entries that can no longer affect a decision, bounding the map against an unbounded-memory DoS: the key is the attacker-controlled username from the unauthenticated login POST, so without a sweep one failed attempt per distinct random username leaks a permanent entry each. An entry is removable when its lock has expired (a future Check would delete it anyway) or when it is unlocked and its failure window has elapsed (a future RecordFailure would reset it to a fresh window). Called periodically by the shared cleanup janitor. Removing a stale entry is behaviorally identical to the lazy reset both hot paths already perform, so it changes no decision.
func (*LoginLimiter) RecordFailure ¶
func (l *LoginLimiter) RecordFailure(username string) bool
RecordFailure registers one failed attempt. Returns true when the account just became locked (so the caller can log the lockout event).
func (*LoginLimiter) RecordSuccess ¶
func (l *LoginLimiter) RecordSuccess(username string)
RecordSuccess clears the failure history for the username.
func (*LoginLimiter) SnapshotAndClear ¶
func (l *LoginLimiter) SnapshotAndClear() func()
SnapshotAndClear captures the current limiter state, replaces it with an empty map, and returns a closure that restores the captured state. It is the exported equivalent of the whitebox snapshot/restore idiom used for test isolation of the package-global limiter: the entries are deep-copied under the mutex and the restore runs under the mutex too, so neither tears against a concurrent reader/writer. Production code never calls this; it exists so package main's test isolation helper does not need access to the unexported entries map across the package boundary (ADR-0002 extraction).