Okay so I'm building a mini SIEM. If you just went "a mini what?" same energy I had a week ago.
A SIEM (Security Information and Event Management system) is basically the thing that watches your logs, notices when something shady is happening, and yells about it. Big companies pay big money for big versions of this. I am making a smol one, called Blue Watch, mostly out of stubbornness and curiosity.
Rule number one I set for myself: config over code. Detection logic should live in a config file, not be hardcoded spaghetti buried in if statements. Future me will thank present me. Probably.
Step zero: fake my own crime scene
Before writing a single line of detection logic, I needed logs to detect things in. Real-world logs are messy and you never actually know the "right answer" β so instead I hand-crafted a single deliberate attack story and wrote it out as a realistic auth.log file, syslog-style timestamps and all:
π΅οΈ An attacker brute-forces their way into an admin account (lots of failed logins, then... success)
π Admin (now compromised) creates a sneaky new user β a classic "leave myself a backdoor" move
π That new backdoor account immediately goes digging somewhere it really shouldn't
π©βπ» Meanwhile, a totally innocent employee logs in from a normal IP at roughly the same time, just doing her job
That last one was the important bit β I needed to know my detector could tell the difference between an attacker and someone just... checking their email.
Day 1: teaching myself regex, the hard way
I will be honest: I did not know regex before this. Now I have opinions about it.
I built a parser that reads raw log lines and pulls structure out of chaos using named capture groups, character classes, and way too much trial and error. Three different line "shapes" (logins, privilege escalations, account creations) each needed their own pattern.
The real win wasn't the regex though β it was writing a normalize() step that takes whatever any of the three patterns spat out and mushes it into one consistent event shape. Doesn't matter which log line it came from, downstream code doesn't need to care.
Bugs squashed along the way: unterminated strings, mysterious NoneType crashes (regex didn't match, oops), off-by-one group numbering, and β my personal favorite β an IndentationError caused by tabs sneaking in next to spaces like a tiny gremlin.
Day 2: rules as data, not dogma
This was the fun part. Instead of writing "if failed login count > 5" buried somewhere in Python, I moved every detection rule into a YAML file. Each rule even gets tagged with a real MITRE ATT&CK technique ID, because if I'm going to pretend to be a security engineer I might as well pretend properly.
Ended up with four rules covering: repeated failed logins, repeated suspicious IP activity, privilege escalation right after login, and β the spiciest one β persistence via account creation (a.k.a. "the attacker just planted a way back in").
Day 3: the detector finally detects something
Two core functions carried the whole day:
- One that groups events by any field a rule asks for (a user, an IP, whatever) and checks it against a threshold β so one function now powers multiple rules instead of copy-pasted near-duplicates.
- One that checks whether event A is followed by event B within some time window, using real datetime math instead of vibes.
And then β the best kind of bug β a false positive. My privilege-escalation rule was matching people to each other's activity just because the timing lined up. My innocent employee's login was accidentally getting blamed for the backdoor account's actions, purely by coincidence of timestamps. Fixed it by adding a same_user toggle to the rule config, since some sequences genuinely need "same person did both things" and others (like sudo β new account) very much don't.
End result: all four rules fired exactly once each, correctly reconstructing the entire attack from first failed login to backdoor to the shadow file β and zero false alarms on the one honest person in the logs. π
What's next
Day 4 is scorer.py β turning a pile of individual alerts into one aggregate threat score per user/IP, so instead of "here are 4 alerts," Blue Watch can say "hey, this person is very clearly the problem."
More soon ποΈπ΅
xoxo
Following along? This is a build-in-public series β feedback, questions, and "actually you should use X instead" comments always welcome.
Top comments (2)
I really like the βrules as dataβ approach. Keeping detection logic in YAML instead of embedding it in code makes the engine much easier to extend and experiment with. One direction Iβd find interesting as Blue Watch grows is moving beyond individual rules toward event correlation. In practice, a brute-force login, account creation, privilege escalation, and sensitive file access become much more valuable when theyβre connected into a single attack chain rather than treated as independent alerts. That also tends to reduce alert fatigue because the analyst sees one high-confidence incident instead of several isolated detections. Looking forward to seeing how the scoring engine evolves, since thatβs often where a SIEM starts feeling like a real investigation tool instead of just a collection of rules.
hey
thanks for your feedback