1-SEC contributors · Reviewed · Module: auth_fortress
SSH Brute-Force Monitoring from Authentication Events
Auth Fortress tracks failed login telemetry by source/account context and produces brute-force or credential-stuffing observations when its configured conditions are met.
What 1-SEC checks
Auth Fortress tracks failed login telemetry by source/account context and produces brute-force or credential-stuffing observations when its configured conditions are met. A trusted authentication-log collector supplies events; module startup alone does not provide SSH log visibility.
Required telemetry and context
Relevant input events: login_failure, auth_failure. Use a trusted producer at the real application boundary.
source_ip (event envelope)username
Field names above describe relevant context, not a complete API request. Consult the current event contract for envelope and detail requirements. Preserve booleans, numbers, and strings as the receiving handler expects them. Agent-provided text cannot independently verify identity or authorization.
Deployment, prevention, and response
Verify collector access to the real auth logs and normalize the original client address through trusted proxy rules where relevant. Review jump hosts and service accounts as benign controls. Keep SSH authentication policy, patching, and recovery access independent of alert processing.
Record the source event, affected agent/host, actual target, policy decision, and response result. Review enforcement configuration separately from detection. A recommendation, selected action, dry-run record, and completed action are different outcomes.
Reproduce the source fixtures
From a checkout of the linked 1-SEC source revision, run the selected fixtures with the repository's Go toolchain. These commands run tests; they do not execute a real attack against your systems.
go test ./internal/modules/auth -run '^(TestLoginTracker_RecordFailure_BruteForce|TestFortress_HandleEvent_LoginFailure|TestLoginTracker_RecordFailure_WindowReset)$' -count=1The referenced source fixtures ran in the selected local package suites. Helper tests and synthetic events establish only the assertions shown, not production prevention or framework compatibility.
TestLoginTracker_RecordFailure_BruteForce
Full fixture and assertions — run in its original package with its helpers.
func TestLoginTracker_RecordFailure_BruteForce(t *testing.T) {
lt := NewLoginTracker(5, 50, 5*time.Minute)
var result LoginResult
for i := 0; i < 5; i++ {
result = lt.RecordFailure("10.0.0.1", "admin")
}
if !result.BruteForce {
t.Error("expected BruteForce after maxPerMinute failures")
}
if result.FailureCount != 5 {
t.Errorf("FailureCount = %d, want 5", result.FailureCount)
}
}TestFortress_HandleEvent_LoginFailure
Full fixture and assertions — run in its original package with its helpers.
func TestFortress_HandleEvent_LoginFailure(t *testing.T) {
cp := makeCapturingPipeline()
f := startedModuleWithPipeline(t, cp)
defer f.Stop()
// Send enough failures to trigger brute force (default maxPerMinute=10)
for i := 0; i < 10; i++ {
ev := core.NewSecurityEvent("test", "login_failure", core.SeverityInfo, "login failed")
ev.Details["username"] = "admin"
ev.SourceIP = "10.0.0.1"
f.HandleEvent(ev)
}
if cp.count() == 0 {
t.Error("expected alert after brute force threshold")
}
if !cp.hasAlertType("brute_force") {
t.Error("expected brute_force alert type")
}
}TestLoginTracker_RecordFailure_WindowReset
Full fixture and assertions — run in its original package with its helpers.
func TestLoginTracker_RecordFailure_WindowReset(t *testing.T) {
lt := NewLoginTracker(5, 50, 5*time.Minute)
// Record some failures
for i := 0; i < 3; i++ {
lt.RecordFailure("10.0.0.1", "admin")
}
// Manipulate the window to simulate time passing
lt.mu.Lock()
rec, _ := lt.failures.Get("10.0.0.1")
rec.window = time.Now().Add(-2 * time.Minute) // push window back >1 minute
lt.mu.Unlock()
// New failure should start fresh window
result := lt.RecordFailure("10.0.0.1", "admin")
if result.FailureCount != 1 {
t.Errorf("FailureCount after window reset = %d, want 1", result.FailureCount)
}
}Tuning, evaluation, and coverage limits
A shared address can represent many legitimate users, and a distributed attack may not look like one noisy source. Successful authentication and failed attempts require separate context. An alert is not evidence that an SSH daemon or network firewall refused a connection.
Evaluate this behavior with representative authorized activity and a controlled known-positive case. Investigate the exact rule, inputs, and state before suppressing an alert. Do not assume a hard-coded heuristic is a configurable setting. Measure your own deployment; the source fixtures do not establish a benchmark, detection rate, compliance certification, or incident-prevention claim.