06. Integrating Wazuh with pfSense, Suricata, and Zeek: Building a Comprehensive Security Stack
Security operations require a centralized view of logs, alerts, and network activities. That's where Wazuh shines—bringing visibility, correlation, and automation in one place. In this guide, I'll walk through how to integrate Wazuh with pfSense (firewall), Suricata (IDS/IPS), and Zeek (network analysis framework).
After implementing these integrations across multiple enterprise environments, I can tell you that proper integration is what separates good security teams from great ones. The tools are powerful individually, but when properly integrated, they create a security monitoring ecosystem that can detect and respond to threats in real-time.
What makes this guide different:
Real production experience with these integrations (not just lab testing)
Common pitfalls and how to avoid them (I've made these mistakes so you don't have to)
Performance considerations for large-scale deployments (this matters more than you'd think)
Troubleshooting techniques for when things go wrong (and they will go wrong)
Let's build a security stack that actually works in the real world.
🔥 pfSense Integration - Your Network Perimeter
Here's why pfSense integration matters: pfSense acts as your gateway firewall, logging every connection attempt, block event, and NAT activity. By sending these logs to Wazuh, you can centralize firewall activity, detect anomalies, and trigger alerts when suspicious patterns emerge.
The reality: Most organizations have a firewall, but they're not getting the full value from their firewall logs. This integration changes that.
Step 1: Configure pfSense to Forward Logs to Wazuh
In the pfSense web interface:
Navigate to: Status → System Logs → Settings
Enable: "Send log messages to remote syslog server"
Set your Wazuh Manager's IP and port:
YOUR_WAZUH_MANAGER_IP:514Select log categories you want to forward:
Firewall logs (essential)
System logs (recommended)
DHCP logs (optional)
VPN logs (if applicable)
Pro tip: Start with firewall logs only. You can expand to other log types as your Wazuh rules mature (and trust me, you'll want to expand once you see the value).
Step 2: Configure Wazuh to Ingest pfSense Logs
On the Wazuh Manager, edit the configuration file:
<!-- Add to /var/ossec/etc/ossec.conf -->
<localfile>
<log_format>syslog</log_format>
<location>/var/log/pfsense.log</location>
</localfile>
Step 3: Create Custom Decoders for pfSense
pfSense logs need proper parsing to extract meaningful fields:
<!-- Custom decoder for pfSense -->
<decoder name="pfsense">
<program_name>^pfsense</program_name>
</decoder>
<decoder name="pfsense-firewall">
<parent>pfsense</parent>
<prematch>filterlog:</prematch>
<regex offset="after_prematch">
^(\S+),(\S+),(\S+),(\S+),(\S+),(\S+),(\S+),(\S+),(\S+),(\S+),(\S+),(\S+),(\S+),(\S+),(\d+),(\S+),(\S+),(\d+),(\S+),(\d+),(\d+),(\S+),(\S+),(\d+),(\d+),(\d+)
</regex>
<order>
timestamp,rule_number,sub_rule_number,anchor,tracker,interface,reason,action,direction,
ip_version,tos,ecn,ttl,id,offset,flags,proto_id,proto,length,src_ip,dst_ip,src_port,
dst_port,data_length,tcp_flags,sequence_number
</order>
</decoder>
Step 4: Create Custom Rules for pfSense
High-priority firewall events:
<rule id="200001" level="5">
<decoded_as>pfsense-firewall</decoded_as>
<field name="action">block</field>
<description>pfSense: Blocked connection attempt</description>
</rule>
<rule id="200002" level="7">
<decoded_as>pfsense-firewall</decoded_as>
<field name="action">block</field>
<field name="proto">tcp</field>
<field name="dst_port">22</field>
<description>pfSense: SSH brute force attempt blocked</description>
</rule>
<rule id="200003" level="10">
<decoded_as>pfsense-firewall</decoded_as>
<field name="action">block</field>
<field name="src_ip">^192\.168\.</field>
<description>pfSense: Internal IP blocked (possible lateral movement)</description>
</rule>
✅ Once applied, Wazuh can now identify specific firewall actions with detailed metadata.
🛡️ Suricata Integration - Deep Packet Inspection
Here's why Suricata integration is powerful: Suricata provides deep packet inspection and intrusion detection. By sending Suricata alerts to Wazuh, you can correlate IDS events with firewall logs for a full picture of threats.
The value: You get network-level visibility that complements your host-based monitoring, and the correlation between the two is where the real insights happen.
Step 1: Configure Suricata to Output EVE JSON Logs
In /etc/suricata/suricata.yaml:
outputs:
- eve-log:
enabled: yes
filetype: regular
filename: eve.json
types:
- alert:
payload: yes
packet: yes
metadata: yes
- http:
extended: yes
- dns:
query: yes
answer: yes
- tls:
extended: yes
- files:
force-magic: no
- smtp:
- ssh
- stats:
totals: yes
threads: no
Step 2: Configure Wazuh to Read Suricata Logs
Edit ossec.conf on the Wazuh Manager:
<!-- Add Suricata logs -->
<localfile>
<log_format>json</log_format>
<location>/var/log/suricata/eve.json</location>
<label key="event_type">suricata</label>
</localfile>
Step 3: Create Suricata Rules in Wazuh
Prioritize high-severity alerts:
<rule id="200100" level="12">
<decoded_as>json</decoded_as>
<field name="event_type">alert</field>
<field name="alert.severity" type="pcre2">^[1-2]$</field>
<description>Suricata: High severity alert</description>
<mitre>
<id>T1055</id>
</mitre>
</rule>
<rule id="200101" level="8">
<decoded_as>json</decoded_as>
<field name="event_type">alert</field>
<field name="alert.category">Trojan</field>
<description>Suricata: Trojan detection</description>
</rule>
<rule id="200102" level="10">
<decoded_as>json</decoded_as>
<field name="event_type">alert</field>
<field name="alert.signature">ET MALWARE</field>
<description>Suricata: Malware signature detected</description>
</rule>
💡 Tip: Adjust severity thresholds based on your network's risk tolerance (and be prepared to tune these as you learn what's normal for your environment).
🔍 Zeek Integration - Network Behavioral Analysis
Here's why Zeek integration is game-changing: Zeek provides behavioral and contextual insights that traditional IDS systems miss. It's like having a network forensics expert watching your traffic 24/7.
The reality: Zeek is incredibly powerful, but it's also complex. The integration with Wazuh makes it much more accessible and actionable.
Prerequisites:
Zeek server with Wazuh agent installed
Understanding of Zeek output (conn.log, http.log, dns.log, etc.)
Proper network configuration for Zeek to monitor traffic
Step 1: Configure Zeek for JSON Output
On the Zeek server, edit the local configuration:
# Edit local.zeek file
sudo nano /opt/zeek/share/zeek/site/local.zeek
Add the following at the bottom:
@load policy/tuning/json-logs
Deploy the changes:
# Apply configuration changes
zeekctl deploy
Verify JSON output:
# Check if logs are in JSON format
head -1 /opt/zeek/logs/current/conn.log
# Should output: {"ts":"2025-01-14T01:23:45Z","uid":"C3lA3m2x","id.orig_h":"192.168.1.5", ...}
Step 2: Configure Wazuh Agent to Read Zeek Logs
Edit the agent configuration:
# Edit ossec.conf on Zeek server
sudo nano /var/ossec/etc/ossec.conf
Add Zeek log collection:
<localfile>
<location>/opt/zeek/logs/current/*.log</location>
<log_format>json</log_format>
</localfile>
Restart the agent:
sudo systemctl restart wazuh-agent
Step 3: Create Zeek Decoders on Wazuh Server
Create custom decoder file:
sudo nano /var/ossec/etc/decoders/0550-zeek_decoders.xml
Add Zeek decoder:
<decoder name="zeek-json">
<prematch>^{\s"ts":\s"\d+.\d+",\s"uid":\s"."\s}$</prematch>
<plugin_decoder>JSON_Decoder</plugin_decoder>
</decoder>
This decoder does two things:
Filtering: Ensures logs are from Zeek by looking for "ts" (timestamp) and "uid" (unique ID) fields
Parsing: Breaks down JSON logs into separate data fields for Wazuh rules (this is where the magic happens)
Step 4: Create Zeek Rules
Create custom rules file:
sudo nano /var/ossec/etc/rules/local_rules.xml
Add Zeek rules:
<!-- Rule 100200: Main Zeek Filter -->
<rule id="100200" level="3">
<decoded_as>json</decoded_as>
<field name="uid">.+</field>
<description>Zeek: Network activity detected</description>
</rule>
<!-- Rule 100201: Network Connection Detector -->
<rule id="100201" level="5">
<if_sid>100200</if_sid>
<field name="id.orig_h">.+</field>
<field name="proto">.+</field>
<description>Zeek: Network connection established</description>
</rule>
<!-- Rule 100202: HTTP Activity -->
<rule id="100202" level="3">
<if_sid>100200</if_sid>
<field name="method">.+</field>
<field name="host">.+</field>
<description>Zeek: HTTP request detected</description>
</rule>
<!-- Rule 100203: DNS Queries -->
<rule id="100203" level="3">
<if_sid>100200</if_sid>
<field name="query">.+</field>
<field name="qtype">.+</field>
<description>Zeek: DNS query detected</description>
</rule>
<!-- Rule 100204: Suspicious Domain -->
<rule id="100204" level="8">
<if_sid>100203</if_sid>
<field name="query" type="pcre2">.*\.(tk|ml|ga|cf)$</field>
<description>Zeek: Suspicious domain query</description>
</rule>
Restart Wazuh Manager:
sudo systemctl restart wazuh-manager
Step 5: Verify Integration
Check if logs are being processed:
# Check if Zeek logs are being read
tail -f /var/ossec/logs/archives/archives.json | grep Zeek
# Check if alerts are being generated
tail -f /var/ossec/logs/alerts/alerts.json | grep "Zeek:"
# Check Filebeat status
tail -f /var/log/filebeat/filebeat
Common Issues and Solutions:
Issue 1: Logs not appearing in Wazuh
Check agent connectivity:
sudo systemctl status wazuh-agentVerify log file permissions:
ls -la /opt/zeek/logs/current/Check agent configuration:
sudo cat /var/ossec/etc/ossec.conf | grep zeek
Issue 2: Alerts not being generated
Verify decoder is working: Check
/var/ossec/logs/archives/archives.jsonTest rule syntax: Use Wazuh's rule testing tools
Check rule dependencies: Ensure parent rules are working
Issue 3: Filebeat transmission issues
Check for nested objects in JSON logs
Verify pipeline configuration
Restart Filebeat:
sudo systemctl restart filebeat
🎯 Advanced Integration Techniques
Log Correlation Rules (the really powerful stuff)
Here's where it gets interesting—correlating events across different sources:
<!-- Rule: Firewall block + Suricata alert -->
<rule id="300001" level="12">
<if_matched_sid>200001,200100</if_matched_sid>
<same_source_ip />
<timeframe>300</timeframe>
<description>Correlated: Firewall block and IDS alert</description>
</rule>
<!-- Rule: Zeek suspicious domain + DNS query -->
<rule id="300002" level="10">
<if_matched_sid>100204,100203</if_matched_sid>
<same_source_ip />
<timeframe>60</timeframe>
<description>Correlated: Suspicious domain query detected</description>
</rule>
Custom Dashboards (the visual stuff)
Here's how to create comprehensive security dashboards that actually tell a story:
{
"title": "Network Security Overview",
"panels": [
{
"title": "Firewall Blocks",
"type": "stat",
"targets": [
{
"query": "rule.id:200001"
}
]
},
{
"title": "Suricata Alerts",
"type": "graph",
"targets": [
{
"query": "rule.id:200100"
}
]
},
{
"title": "Zeek Network Activity",
"type": "table",
"targets": [
{
"query": "rule.id:100201"
}
]
}
]
}
Automated Response (the automation stuff)
Here's how to set up automated responses to threats (this is where it gets really cool):
#!/bin/bash
# Automated response script
# Get high-priority alerts
ALERTS=$(curl -k -s -u admin:password "https://YOUR_WAZUH_IP:55000/events" | jq -r '.data.affected_items[] | select(.rule.level >= 10) | .agent')
# Block IPs in pfSense
for agent in $ALERTS; do
# Extract source IP
SRC_IP=$(curl -k -s -u admin:password "https://YOUR_WAZUH_IP:55000/events" | jq -r '.data.affected_items[] | select(.agent == "'$agent'") | .data.srcip')
# Add firewall rule
ssh admin@pfSense "pfctl -t blocked_ips -T add $SRC_IP"
done
📊 Performance Optimization
Resource Management
Indexer Performance:
# OpenSearch configuration
opensearch.yml:
indices.memory.index_buffer_size: 30%
indices.queries.cache.size: 20%
indices.fielddata.cache.size: 20%
Manager Performance:
<!-- Wazuh Manager configuration -->
<ossec_config>
<analysisd>
<log_alert_level>3</log_alert_level>
<log_analysis_level>3</log_analysis_level>
</analysisd>
</ossec_config>
Log Retention Policies
Set appropriate retention periods:
# Index lifecycle management
policy:
phases:
hot:
actions:
rollover:
max_size: 50GB
max_age: 1d
warm:
min_age: 1d
actions:
forcemerge:
max_num_segments: 1
cold:
min_age: 7d
actions:
freeze: {}
delete:
min_age: 30d
🚨 Troubleshooting Common Issues
Integration Failures
Issue: pfSense logs not appearing
# Check syslog configuration
grep -r "pfsense" /var/log/
# Verify network connectivity
telnet YOUR_WAZUH_IP 514
# Check Wazuh configuration
sudo cat /var/ossec/etc/ossec.conf | grep pfsense
Issue: Suricata alerts not correlating
# Check JSON format
head -1 /var/log/suricata/eve.json
# Verify Wazuh decoder
sudo /var/ossec/bin/wazuh-logtest
# Check rule syntax
sudo /var/ossec/bin/wazuh-rule-test
Issue: Zeek integration not working
# Check agent status
sudo systemctl status wazuh-agent
# Verify log file permissions
ls -la /opt/zeek/logs/current/
# Check decoder configuration
sudo cat /var/ossec/etc/decoders/0550-zeek_decoders.xml
Performance Issues
Issue: High CPU usage
# Check process usage
top -p $(pgrep wazuh-analysisd)
# Monitor indexer performance
curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"
# Check log volume
du -sh /var/ossec/logs/
Issue: Memory issues
# Check memory usage
free -h
# Monitor JVM heap
curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"
# Check indexer memory
curl -X GET "localhost:9200/_cluster/health?pretty"
🎉 Final Thoughts
By integrating pfSense, Suricata, and Zeek with Wazuh, you're building a layered defense strategy that actually works:
pfSense gives you perimeter control and network-level visibility
Suricata adds deep packet inspection & IDS alerts for threat detection
Zeek provides behavioral and contextual insights for advanced analysis
When all three feed into Wazuh, you gain correlated, actionable intelligence—turning logs into meaningful alerts and responses.
What you've accomplished:
Integrated network security tools with Wazuh
Created custom decoders and rules for each tool
Set up log correlation across different sources
Implemented performance optimization techniques
Next steps:
Fine-tune rules - Adjust based on your environment (this takes time)
Create custom dashboards - Visualize your security posture (this is actually fun)
Set up automated responses - Respond to threats automatically (this is where it gets really cool)
Monitor performance - Ensure optimal operation (this is crucial)
Pro tip: Start with basic integration and gradually add complexity. This allows you to identify and resolve issues before they become problems (and trust me, you'll have issues).
🔜 What's Next?
Now that you have a comprehensive security monitoring stack, it's time to put it to work. In the next chapter, we'll explore advanced threat hunting techniques and incident response procedures.
You'll learn:
How to hunt for advanced threats
Incident response workflows
Forensic analysis techniques
Building a security operations center





