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.

Read the implementation: handleLoginFailure

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=1

The 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.

Related implemented controls

Implementation reviewed at source revision 2af8ea9dab2057a1ffc3cce9bf44220271fab18e. Recheck the actual release and integration before deployment. Sources and corrections policy.