djspace

A SIEM-lite for a home network in one morning

October 22, 2026 · 5 min read · loki, grafana, unifi, ids, detection

My home gateway has an intrusion prevention system (IPS) and a lot of logs, but reading them meant clicking through its own UI, exporting CSVs and guessing. Its local log storage is small and rotates quickly, and the gateway only has 3 GB of RAM to begin with. I wanted three things:

  1. An email the moment the IPS sees something, with the signature, the device and the destination in the subject.
  2. History I control, searchable for at least 30 days.
  3. The gateway’s events and my server’s own logs in one place, so I can line up a firewall block with what the server saw at the same second.

A full SIEM (Wazuh, Graylog, Security Onion) would have done it, at the cost of 4–16 GB of RAM, an OpenSearch cluster and agents everywhere. That’s built for fleets, not a house. I already ran Grafana on a Linux server for metrics, so I added logs to it.

The pipeline

The pipeline: gateway events over syslog and the server journal flow through rsyslog and Grafana Alloy into Loki; Grafana searches them, shows a dashboard and sends alert emails.

PieceRoleWhy this choice
rsyslogreceives the gateway’s syslog on a high UDP port and writes a plain fileThe gateway’s syslog isn’t strict RFC 3164/5424; rsyslog accepts it as is. The plain file doubles as a grep fallback when Grafana is down. A port above 1024 means no root needed.
Grafana Alloytails that file and reads the systemd journalOne agent for both sources. Labels kept few (job, host, unit, level); everything else is parsed at query time.
Lokistores logs, 30-day retentionSingle binary, local disk, listens on localhost only. About 190 MB of RAM; Alloy about 260 MB.
GrafanaExplore, a dashboard, alert rulesAlready there for metrics, so no new web app to secure.

The host firewall only accepts the syslog port from the gateway’s address. Loki and Alloy’s UI bind to 127.0.0.1, and both have usage reporting turned off. Everything is installed by one script that dry-runs by default and prints its own rollback.

What the gateway actually sends

The first surprise: the gateway’s “SIEM server” setting sends events, not traffic. Its per-connection flow log stays in its own UI. At a quiet hour I got one line in five minutes. What does arrive is rich CEF:

CEF:0|Ubiquiti|UniFi Network|10.x|200|Threat Detected|7|UNIFIcategory=Security
  act=allowed UNIFIrisk=medium UNIFIdirection=outgoing
  UNIFIsrcClientHostname=<server> UNIFIdstDomain=testmyids.com
  UNIFIipsSignature=GPL ATTACK_RESPONSE id check returned root UNIFIipsSignatureId=2100498

Two parsing notes:

Testing an IDS alert without waiting for an attack

I didn’t want to write the alert against a guessed format. A real detection, on demand, takes one command:

curl http://testmyids.com
# uid=0(root) gid=0(root) groups=0(root)

That response matches a long-standing Emerging Threats signature (“GPL ATTACK_RESPONSE id check returned root”). The line reached Loki seconds later, and I wrote the alert against its real fields:

sum by (signature, risk, action, client, domain, src, dst, port, direction) (
  count_over_time({job="gateway"} |= "UNIFIcategory=Security"
    | regexp "UNIFIipsSignature=(?P<signature>.+?) UNIFI"
    | regexp "UNIFIrisk=(?P<risk>\\S+)"
    | regexp "act=(?P<action>\\S+)"
    ... [10m]))

Each label combination becomes its own alert instance, so the email subject names the signature and the device. A second run of the curl produced the email.

Timeline of the test: curl, IPS signature, event in Loki, alert evaluation, email; then the same test in block mode times out.

What it found on day one

Defense in depth: the service rejected the plain-HTTP request first, the IPS blocked the source two seconds later, and a network jail limits the service if both ever fail.

Mistakes worth keeping

Results