Your logs are everywhere and you’re flying blind
Here’s the thing: if you’ve got more than three Linux machines in your home lab, you’re already losing the security plot. Logs are scattered across syslog, application logs, firewall logs, auth logs — and when something goes sideways at 2 AM, you’re SSHing into five different boxes trying to piece together what actually happened.
It’s like hiring a forklift to move a couch. Technically you could manage everything manually, but your future self at 2 AM will hate you.
That’s where Wazuh comes in. It’s an open-source SIEM (Security Information and Event Management) that does way more than just centralize logs. Out of the box, you get:
- SIEM — aggregates and indexes logs from every machine
- HIDS (Host-based Intrusion Detection System) — detects suspicious activity on your hosts
- FIM (File Integrity Monitoring) — alerts when critical files change
- Vulnerability scanning — finds CVEs in your systems
- Threat detection — built-in rules for common attacks (brute force, rootkits, etc.)
One stack. No duct tape. No buying five different SaaS tools.
Let’s get it running in Docker.
The Wazuh stack: three containers, one pain point solved
Wazuh is built on three components: the manager (brains), the indexer (storage), and the dashboard (the pretty pictures). We’re going to run all three via Docker Compose.
version: '3.8'
services: wazuh-indexer: image: docker.io/wazuh/wazuh-indexer:4.7.0 container_name: wazuh-indexer environment: - discovery.type=single-node - INDEXER_SECURITY_SSL_ENABLED=true - INDEXER_SECURITY_SSL_VERIFICATION_MODE=full - INDEXER_SECURITY_SSL_KEYSTORE_FILEPATH=/usr/share/wazuh-indexer/certs/node-key.pem - INDEXER_SECURITY_SSL_CERTIFICATE_FILEPATH=/usr/share/wazuh-indexer/certs/node-cert.pem - INDEXER_SECURITY_SSL_CERTIFICATEAUTHORITIES_FILEPATH=/usr/share/wazuh-indexer/certs/root-ca.pem - INDEXER_ADMIN_IDENTITY_FILE=/usr/share/wazuh-indexer/certs/admin-identity.xml - INDEXER_PLUGINS_SECURITY_SSL_HTTP_KEYSTORE_FILEPATH=/usr/share/wazuh-indexer/certs/node-key.pem - INDEXER_PLUGINS_SECURITY_SSL_HTTP_CERTIFICATE_FILEPATH=/usr/share/wazuh-indexer/certs/node-cert.pem - INDEXER_PLUGINS_SECURITY_SSL_HTTP_CERTIFICATEAUTHORITIES_FILEPATH=/usr/share/wazuh-indexer/certs/root-ca.pem volumes: - wazuh-indexer-data:/var/lib/wazuh-indexer - ./certs/indexer-node1.pem:/usr/share/wazuh-indexer/certs/node-cert.pem:ro - ./certs/indexer-node1-key.pem:/usr/share/wazuh-indexer/certs/node-key.pem:ro - ./certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem:ro - ./certs/admin-identity.xml:/usr/share/wazuh-indexer/certs/admin-identity.xml:ro ports: - "9200:9200" networks: - wazuh-net healthcheck: test: curl -s https://localhost:9200 -k -u admin:admin >/dev/null 2>&1 interval: 30s timeout: 10s retries: 5
wazuh-manager: image: docker.io/wazuh/wazuh-manager:4.7.0 container_name: wazuh-manager environment: - INDEXER_URL=https://wazuh-indexer:9200 - INDEXER_USERNAME=admin - INDEXER_PASSWORD=admin - FILEBEAT_SSL_VERIFICATION_MODE=full - SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/certs/root-ca.pem - SSL_CERTIFICATE=/etc/ssl/certs/manager.pem - SSL_KEY=/etc/ssl/certs/manager-key.pem volumes: - wazuh-manager-data:/var/ossec/data - wazuh-manager-etc:/var/ossec/etc - ./certs/root-ca.pem:/etc/ssl/certs/root-ca.pem:ro - ./certs/manager.pem:/etc/ssl/certs/manager.pem:ro - ./certs/manager-key.pem:/etc/ssl/certs/manager-key.pem:ro ports: - "1514:1514" - "1515:1515" networks: - wazuh-net depends_on: - wazuh-indexer healthcheck: test: curl -s https://localhost:55000 -k -u wazuh:wazuh >/dev/null 2>&1 interval: 30s timeout: 10s retries: 5
wazuh-dashboard: image: docker.io/wazuh/wazuh-dashboard:4.7.0 container_name: wazuh-dashboard environment: - INDEXER_USERNAME=admin - INDEXER_PASSWORD=admin - WAZUH_API_URL=https://wazuh-manager:55000 - WAZUH_API_USERNAME=wazuh - WAZUH_API_PASSWORD=wazuh ports: - "443:443" networks: - wazuh-net depends_on: - wazuh-indexer - wazuh-manager volumes: - ./certs/dashboard.pem:/usr/share/wazuh-dashboard/certs/dashboard.pem:ro - ./certs/dashboard-key.pem:/usr/share/wazuh-dashboard/certs/dashboard-key.pem:ro
volumes: wazuh-indexer-data: wazuh-manager-data: wazuh-manager-etc:
networks: wazuh-net: driver: bridgeBefore you docker compose up, you need certificates. The Wazuh project provides a cert-generation script. Download it from their GitHub, run it to generate certs, then you’re good to go.
After the stack is healthy (check those health checks), hit https://localhost in your browser. Default creds are admin:admin for the dashboard. Change them immediately.
Getting your Linux host talking to Wazuh
Install the agent on any Linux host you want to monitor:
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | apt-key add -echo "deb https://packages.wazuh.com/4.x/apt/ stable main" | tee /etc/apt/sources.list.d/wazuh.listapt update && apt install -y wazuh-agentThen enroll it with your Wazuh manager:
/var/ossec/bin/wazuh-control startThe agent auto-registers if you’ve configured the manager to accept auto-enrollment (it’s on by default). Check the dashboard under Agents — you should see your host within a minute.
File integrity monitoring: catch the sneaky changes
FIM is the feature that’ll actually wake you up at 2 AM because something changed that shouldn’t have. We’re going to monitor /etc/ and /home/ for unauthorized edits.
Add this to /var/ossec/etc/ossec.conf on the manager (inside the <ossec_config> block):
<syscheck> <frequency>3600</frequency> <directories check_all="yes" realtime="yes">/etc</directories> <directories check_all="yes" realtime="yes">/home</directories> <ignore>/etc/mtab</ignore> <ignore>/etc/resolv.conf</ignore> <alert_new_files>yes</alert_new_files></syscheck>The realtime="yes" means Wazuh checks those directories every time something changes, instead of waiting for the hourly scan. On a busy system, this might be noisy — dial it back to /etc only if you’re drowning in alerts.
Restart the manager and agent. Now when someone (or some script) modifies /etc/sudoers or adds a cron job to /var/spool/cron/, you’ll get an alert.
Tuning alerts: your first lesson in not crying wolf
Day one with Wazuh, you’re going to get flooded with alerts. Legitimate ones, sure, but also a ton of noise. Your automated patching routine is firing alerts. Docker is restarting containers. Cron is doing its thing.
Here’s how to suppress a false positive. Let’s say you’re getting hammered with alerts every time sudo runs with password entry — which is normal and expected.
Create a file /var/ossec/etc/rules/local_rules.xml:
<group name="local,"> <rule id="100001" level="0"> <if_sid>5402</if_sid> <match>sudo.*password</match> <description>Suppress sudo password entry alerts</description> </rule></group>Rule ID 5402 is Wazuh’s built-in “sudo” rule. By creating a rule with level="0" (zero priority), you’re telling it to ignore that match. Restart, and the noise stops.
Spend your first week tuning. Your second week, you’ll have actual signal.
Your first real alert
You’ll know Wazuh is working when you see something like this in the dashboard:
- Alert Level: 10 (critical)
- Rule: Rootkit Detection — possible rootkit found
- Agent: homelab-docker-01
- Details: AIDE integrity check failed on
/usr/lib/modules
This is the moment you care about. This is why you’re running a SIEM. Your 2 AM self is now equipped to make decisions instead of guessing.
One last thing
Wazuh won’t replace proper backups, patching, and access control. It’s the detective, not the cop. But honestly? Running a SIEM in your home lab makes you dramatically smarter about what’s actually happening on your machines. You stop flying blind.
Your 2 AM self will appreciate it.