Building a home SOC with Wazuh
I wanted to see attacks from the defender’s side, so I built a small SOC at home: a Wazuh SIEM watching my server, plus an isolated, deliberately vulnerable lab to attack. Then I checked what it caught and what it missed.
Wazuh 4.14.9 plus an isolated attack lab
- SIEM: single-node Wazuh 4.14.9 (manager, indexer, dashboard) in Docker, with my homelab server enrolled as a monitored endpoint.
- Telemetry: SSH/sudo/PAM auth logs, ufw firewall logs, syslog, Docker engine events, real-time file integrity monitoring on
/etc, the CIS Ubuntu 24.04 benchmark, and the lab’s web access logs. - Attack lab: DVWA and OWASP Juice Shop on a Docker
internalnetwork with no internet access (an egress test from the DVWA container was blocked). Only an nginx proxy is reachable, and only from the server or over Tailscale. The lab is off unless I start it withlab up.
A security tool shouldn’t be the weak spot
- Every published port is bound to specific interfaces (localhost, LAN, Tailscale) and never
0.0.0.0, because Docker-published ports bypass ufw. I verified it withss. - The indexer and the Wazuh API are localhost-only, and the unused syslog port isn’t published at all.
- I replaced all the default demo passwords with random 26-character secrets before first boot and confirmed the old defaults are rejected (HTTP 401).
What Wazuh detected
| Test | Result | Rules |
|---|---|---|
| 6 failed SSH logins as a nonexistent user | Detected: 11 alerts + 1 brute-force correlation (level 10) | 5710 → 5712 |
Create, modify and delete a test file in /etc | Detected within seconds | 554 550 553 |
SQLi: UNION SELECT user,password FROM users | Detected (attack succeeded) | 31106 |
SQLi: 1' OR '1'='1 | Missed (attack succeeded, no alert) | none |
| CIS Ubuntu 24.04 benchmark | 50% on the first scan | 19003 |
Triage on real noise
While I was setting things up, a Greenbone rescan of the server was running. Its default-credential SSH checks produced 735 rule 5710 alerts and 1 level-10 brute-force alert within about a minute. The SIEM traced the activity to the scanner’s container, so I could confirm it was my own authorized scan and not an attack. That’s the everyday triage an analyst does: separating expected activity from real threats.
Signatures have gaps
Both SQL injections worked against DVWA. Wazuh caught the UNION-based one with rule 31106 (“A web attack returned code 200”) but raised nothing for the boolean OR '1'='1' variant, even though it returned every user row. My next steps are a custom Wazuh rule for that pattern, or a WAF like ModSecurity with the OWASP Core Rule Set in front of the lab.
Configs (with secrets replaced by placeholders): github.com/oemarino/homelab.
What I learned
- Test your detections. I only know about the SQLi gap because I attacked my own lab and checked for the alert.
- Correlation beats volume. One level-10 brute-force alert (5712) is more useful than hundreds of individual failed logins.
- Context makes triage fast. Knowing which container generated the traffic turned 735 alerts into a 1-minute investigation.
- Isolation first. Vulnerable apps belong on a network with no internet egress, switched off when I’m not using them.