add ipabusedb-docker-ssh: report sshd brute-force attempts from docker logs to AbuseIPDB
This commit is contained in:
1 parent
fcb956124b
commit
21bda80f88
8 files changed
+658
No files matched your search
@@ -0,0 +1,119 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestParseExampleLines(t *testing.T) {
|
||||
data, err := os.ReadFile("loglines_example.txt")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p := newParser(time.Time{})
|
||||
if err := p.ingest(bytes.NewReader(data)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := map[string]int{
|
||||
"41.93.28.11": 2,
|
||||
"45.191.4.252": 2,
|
||||
"164.77.161.180": 2,
|
||||
"212.128.3.152": 2,
|
||||
}
|
||||
if len(p.events) != len(want) {
|
||||
t.Fatalf("got %d events, want %d", len(p.events), len(want))
|
||||
}
|
||||
for ipStr, count := range want {
|
||||
ev, ok := p.events[netip.MustParseAddr(ipStr)]
|
||||
if !ok {
|
||||
t.Errorf("missing event for %s", ipStr)
|
||||
continue
|
||||
}
|
||||
if ev.count != count {
|
||||
t.Errorf("%s: count %d, want %d", ipStr, ev.count, count)
|
||||
}
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
if err := writeCSV(&out, p.sorted()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lines := strings.Split(strings.TrimSpace(out.String()), "\n")
|
||||
if lines[0] != "IP,Categories,Comment,ReportDate" {
|
||||
t.Errorf("bad header: %q", lines[0])
|
||||
}
|
||||
if len(lines) != 5 {
|
||||
t.Fatalf("got %d csv lines, want 5:\n%s", len(lines), out.String())
|
||||
}
|
||||
if !strings.Contains(lines[1], "41.93.28.11,\"18,22\",") || !strings.HasSuffix(lines[1], "2026-10-06T12:04:24Z") {
|
||||
t.Errorf("unexpected first row: %q", lines[1])
|
||||
}
|
||||
// The descriptive "Invalid user ... from" line should win over the
|
||||
// later "Connection closed by invalid user" line for the comment.
|
||||
if !strings.Contains(lines[2], ",Invalid user production from 45.191.4.252 port 38924 (2 events),") {
|
||||
t.Errorf("unexpected comment in row: %q", lines[2])
|
||||
}
|
||||
// AllowUsers rejections are reported with their own message.
|
||||
if !strings.Contains(lines[1], "User root from 41.93.28.11 not allowed because not listed in AllowUsers (2 events)") {
|
||||
t.Errorf("unexpected comment in row: %q", lines[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestExclusions(t *testing.T) {
|
||||
p := newParser(time.Time{})
|
||||
for _, line := range []string{
|
||||
"2026-10-06T12:04:24.687263829Z Invalid user git from 1.2.3.4 port 38924",
|
||||
"2026-10-06T12:04:24.687263829Z User git from 1.2.3.5 not allowed because not listed in AllowUsers",
|
||||
"2026-10-06T12:04:24.687263829Z Invalid user bob from 172.19.0.1 port 38924",
|
||||
"2026-10-06T12:04:24.687263829Z Invalid user bob from 10.0.0.9 port 38924",
|
||||
"2026-10-06T12:04:24.687263829Z Invalid user bob from 127.0.0.1 port 38924",
|
||||
"2026-10-06T12:04:24.687263829Z Invalid user bob from fd00::1 port 38924",
|
||||
"2026-10-06T12:04:24.687263829Z Invalid user bob from not-an-ip port 38924",
|
||||
"2026-10-06T12:04:24.687263829Z Received disconnect from 8.8.8.8 port 53534:11: disconnected by user",
|
||||
"garbage line without timestamp",
|
||||
} {
|
||||
p.handleLine(line)
|
||||
}
|
||||
if len(p.events) != 0 {
|
||||
t.Errorf("expected no events, got %v", p.events)
|
||||
}
|
||||
if p.skipped != 1 {
|
||||
t.Errorf("skipped = %d, want 1", p.skipped)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSinceCutoff(t *testing.T) {
|
||||
cutoff := time.Date(2026, 10, 6, 12, 4, 29, 0, time.UTC)
|
||||
p := newParser(cutoff)
|
||||
p.handleLine("2026-10-06T12:04:24.687263829Z Invalid user production from 45.191.4.252 port 38924")
|
||||
p.handleLine("2026-10-06T12:04:30.222573149Z Invalid user production from 212.128.3.152 port 60626")
|
||||
if len(p.events) != 1 {
|
||||
t.Fatalf("got %d events, want 1", len(p.events))
|
||||
}
|
||||
if _, ok := p.events[netip.MustParseAddr("212.128.3.152")]; !ok {
|
||||
t.Error("expected only the post-cutoff event to remain")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPv6AndLatestWins(t *testing.T) {
|
||||
p := newParser(time.Time{})
|
||||
p.handleLine("2026-10-06T12:04:24Z Invalid user a from 2001:db8::1 port 1")
|
||||
p.handleLine("2026-10-06T12:04:26Z Invalid user b from 2001:db8::1 port 2")
|
||||
p.handleLine("2026-10-06T12:04:25Z Invalid user c from 2001:db8::1 port 3")
|
||||
ev := p.events[netip.MustParseAddr("2001:db8::1")]
|
||||
if ev == nil || ev.count != 3 || ev.user != "b" {
|
||||
t.Fatalf("unexpected event: %+v", ev)
|
||||
}
|
||||
var out bytes.Buffer
|
||||
if err := writeCSV(&out, p.sorted()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(out.String(), "(3 events)") {
|
||||
t.Errorf("expected event count in comment:\n%s", out.String())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user