ipabusedb-docker-reporter: rename from ipabusedb-docker-ssh, add Dockerfile with runit service

Build on git.eeqj.de/sneak/baseimage:2026-11-05 with the docker CLI
copied from docker:cli. A runit service sleeps INTERVAL seconds
(default 1200), then runs the reporter with --since INTERVAL --submit;
DOCKER_CONTAINER_NAME and IPABUSEDB_API_KEY come from the container
environment. Add .dockerignore, make image, and README section; fix the
binary name in .gitignore.
This commit is contained in:
sneak committed 2026-11-06 20:18:26 -08:00
1 parent b8c36689ae
commit 1aa7d84401
16 files changed
+135 -11

No files matched your search

+119
View File
@@ -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())
}
}