Firewall logs are the most valuable telemetry your SIEM isn't getting. This guide covers syslog forwarding from Palo Alto PAN-OS, Fortinet FortiOS, and pfSense into Wazuh, decoders, correlation rules, and cross-platform alerting.
This is the gap I keep closing for clients. Firewall logs into Wazuh. Three firewalls, one approach. Palo Alto, FortiGate, pfSense. Here's the integration flow and the decoder configs you need.
The Integration Architecture
UDP 514 (syslog)
┌───────────────────────────────────────────────┐
│ Wazuh Manager │
│ │
│ paloalto_decoder fortigate_decoder │
│ pfsense_decoder trendmicro_decoder │
│ │
│ ossec.conf: │
│ │
│ syslog │
│ 514 │
│ udp │
│ │
└───┬──────────┬──────────┬─────────────────────┘
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ Palo │ │Forti │ │pfSense │
│ Alto │ │Gate │ │ │
│PAN-OS │ │FortiOS │ │FreeBSD │
│--------│ │--------│ │--------│
│TRAFFIC │ │Forward │ │Filter │
│THREAT │ │Local │ │Suricata│
│WildFire│ │UTM IPS │ │pfBlock │
│URL Fil │ │App Ctl │ │OpenVPN │
│User-ID │ │Web Fil │ │ │
│HIP Mtch│ │DNS Fil │ │ │
│--------│ │--------│ │--------│
│CEF fmt │ │CEF fmt │ │BSD fmt │
└────────┘ └────────┘ └────────┘
Important detail people skip: Wazuh Manager needs syslog enabled in ossec.conf. By default it only listens for agent traffic on ports 1514-1516. You have to explicitly add the <remote> block above for syslog on UDP 514. Without it, the manager drops firewall packets silently. I've spent hours debugging this on client deployments where everything looked right on the firewall side but Wazuh was simply not listening.
Palo Alto
Palo Alto logs come in two clearly separated streams: TRAFFIC and THREAT. TRAFFIC is every session, allow or deny. THREAT is when the security profiles catch something. You want both. The syslog server profile is under Device → Server Profiles → Syslog. UDP 514, BSD format. Check every log type except HIP Match unless you're running GlobalProtect with host checks.
The TRAFFIC log format is fairly predictable. Fixed-width fields separated by commas. Source IP at position 6, destination at 9, application at 14, action at 10. The decoder below handles the common case.
<decoder name="paloalto-threat">
<prematch type="pcre2">THREAT\s+\w+</prematch>
</decoder>
<decoder name="paloalto-threat-detail">
<parent>paloalto-threat</parent>
<regex>THREAT\s+(\w+)\s+(\d+/\d+/\d+\s+\d+:\d+:\d+)</regex>
<regex>src=(\S+)\s+dst=(\S+)\s+srcuser=(\S+)\s+rule="([^"]+)"</regex>
<order>threat_type,timestamp,srcip,dstip,user,rule_name</order>
</decoder>
<decoder name="paloalto-traffic">
<prematch type="pcre2">TRAFFIC\s+\w+</prematch>
</decoder>
<decoder name="paloalto-traffic-detail">
<parent>paloalto-traffic</parent>
<regex>src=(\S+)\s+dst=(\S+)\s+dport=(\d+)\s+app="([^"]+)"\s+action="([^"]+)"</regex>
<order>srcip,dstip,dstport,application,action</order>
</decoder>
<rule id="100080" level="14">
<decoded_as>paloalto-threat</decoded_as>
<field name="threat_type" type="pcre2">vulnerability|exploit|spyware</field>
<description>Palo Alto: $(threat_type) threat from $(srcip) → $(dstip)</description>
<group>paloalto,threat,</group>
</rule>
<rule id="100081" level="10">
<decoded_as>paloalto-traffic</decoded_as>
<field name="action">deny</field>
<frequency="20" timeframe="60"><same_source_ip />
<description>Palo Alto: 20+ denies from $(srcip) in 1 min — scanning</description>
<group>paloalto,scanning,</group>
</rule>
Fortinet FortiGate
FortiGate config is CLI-based. The web UI exposes maybe 70% of what you need. For full UTM event forwarding (IPS, AV, App Control, Web Filter, DNS Filter), you need the CLI filter block. CEF format is the most reliable, FortiOS CEF output is ugly but consistent, which is exactly what Wazuh's regex decoders need.
config log syslogd setting
set status enable
set server "WAZUH_MANAGER_IP"
set port 514
set facility local4
set format cef
end
config log syslogd filter
set severity information
set forward-traffic enable
set local-traffic enable
set anomaly enable
set dns enable
set ssh enable
end
<decoder name="fortigate">
<prematch type="pcre2">\|Fortinet\|FortiGate\|</prematch>
</decoder>
<decoder name="fortigate-detail">
<parent>fortigate</parent>
<regex type="pcre2">\|Fortinet\|FortiGate\|([^\|]+)\|([^\|]+)\|(\d+)\|</regex>
<order>event_type,event_name,severity</order>
</decoder>
<decoder name="fortigate-network">
<parent>fortigate</parent>
<regex type="pcre2">src=(\S+)\s+spt=(\d+)\s+dst=(\S+)\s+dpt=(\d+)\s+act=(\S+)</regex>
<order>srcip,srcport,dstip,dstport,action</order>
</decoder>
<rule id="100090" level="14">
<decoded_as>fortigate</decoded_as>
<field name="event_type" type="pcre2">ips|virus</field>
<description>FortiGate: $(event_type) block — $(event_name) from $(srcip)</description>
<group>fortigate,threat,</group>
</rule>
pfSense
pfSense is the wildcard. It's everywhere, branch offices, homelabs that became production, small government sites. The filter log format is basically a CSV spewed into syslog. Parsing it is annoying but reliable once your regex is right. If you're running Suricata in IPS mode on pfSense, forward those alerts too, they're much higher signal than raw filter logs.
<decoder name="pfsense-filter">
<prematch type="pcre2">filterlog:</prematch>
</decoder>
<decoder name="pfsense-filter-detail">
<parent>pfsense-filter</parent>
<regex type="pcre2">filterlog:.*?,([a-z]+),.*?,([\d.]+),.*?,([\d.]+),.*?,(\d+),.*?,(\d+),.*?,(\w+)</regex>
<order>action,srcip,dstip,srcport,dstport,protocol</order>
</decoder>
<rule id="100095" level="12">
<decoded_as>pfsense-filter</decoded_as>
<field name="action">block</field>
<frequency="15" timeframe="30"><same_source_ip />
<description>pfSense: 15+ blocks from $(srcip) in 30s — scanning</description>
<group>pfsense,scanning,</group>
</rule>
Trend Micro: Apex One & Deep Security
Trend Micro isn't a firewall. But it belongs in the same conversation because Apex One and Deep Security generate endpoint and server telemetry that firewalls miss. A Palo Alto can block an IP. Apex One can tell you which process on which endpoint downloaded the payload from that IP before the block fired.
Apex One → Wazuh
Step 1: Enable Syslog Forwarding on Apex One
Apex One's web console, Administration → Syslog Settings. Check Enable syslog. Set server type to CEF (Common Event Format). Point it at your Wazuh manager's IP on UDP 514.
Under Log Type, check these categories:
- Virus/Malware: Every detection, block, quarantine action. This is your primary signal for endpoint compromise.
- Spyware/Grayware: PUA detections, adware, tracking software. Lower severity but useful for compliance reporting and spotting shadow IT.
- Firewall Violation: Apex One has a built-in host firewall. Blocked inbound/outbound connections. Correlate these with Wazuh's own network alerts.
- Web Reputation: Blocked URLs. Which endpoints are hitting known-malicious domains. Cross-reference with your proxy/DNS logs for the full picture.
- Behavioral Monitoring: The most interesting category. Apex One's machine learning engine flags suspicious process behavior before there's a known signature. These are canaries for zero-day or fileless attacks. If you only enable one category beyond malware, make it this one.
- Device Control: USB block events. Which users are plugging in random drives. Internal policy enforcement, not threat detection, but useful in regulated environments.
The CEF header Apex One generates looks like this:
CEF:0|Trend Micro|Apex One|14.0|1000001|Virus detected|5|src=192.168.1.45 suser=DESKTOP-01\jdoe dhost=DESKTOP-01 filePath=C:\\Windows\\Temp\\malware.exe fileHash=ab12cd34ef567890ab12cd34ef567890
The pipe-delimited prefix (|Trend Micro|Apex One|version|eventId|name|severity|) is standard CEF. Everything after is key-value pairs. Our decoder regex targets this structure.
Step 2: Decoders
Three decoders, chained by parent relationship. The first matches the CEF program name. The second extracts event metadata from the pipe-delimited prefix. The third pulls endpoint details from the key-value extension fields.
<decoder name="trendmicro-apex">
<program_name>Trend Micro Apex One</program_name>
</decoder>
<decoder name="trendmicro-apex-detail">
<parent>trendmicro-apex</parent>
<regex type="pcre2">\|Trend Micro\|Apex One\|([^\|]+)\|([^\|]+)\|(\d+)\|</regex>
<order>event_type,threat_name,severity</order>
</decoder>
<decoder name="trendmicro-apex-endpoint">
<parent>trendmicro-apex</parent>
<regex type="pcre2">src=(\S+)\s+suser=(\S+)\s+dhost=(\S+)</regex>
<order>srcip,username,hostname</order>
</decoder>
<decoder name="trendmicro-apex-file">
<parent>trendmicro-apex</parent>
<regex type="pcre2">filePath=(\S+)\s+fileHash=(\S+)</regex>
<order>file_path,file_hash</order>
</decoder>
Notice the parent chain. When a log arrives matching trendmicro-apex, Wazuh runs all child decoders against it. This means trendmicro-apex-detail and trendmicro-apex-endpoint both fire on the same event, extracting different fields. This is how you build layered parsing without one monster regex.
Step 3: Alert Rules
<rule id="100050" level="14">
<decoded_as>trendmicro-apex</decoded_as>
<field name="event_type" type="pcre2">Virus|Ransomware|Malware</field>
<description>Trend Micro Apex One: $(event_type) — $(threat_name) on $(hostname)</description>
<group>trendmicro,malware_detected,</group>
</rule>
<rule id="100051" level="10">
<decoded_as>trendmicro-apex</decoded_as>
<field name="event_type" type="pcre2">Spyware|Grayware</field>
<description>Trend Micro Apex One: PUA detection — $(threat_name) on $(hostname)</description>
<group>trendmicro,pua_detected,</group>
</rule>
<rule id="100052" level="12">
<decoded_as>trendmicro-apex</decoded_as>
<field name="event_type" type="pcre2">Behavioral|Suspicious</field>
<description>Trend Micro Apex One: Behavioral alert on $(hostname)</description>
<group>trendmicro,behavioral_alert,</group>
</rule>
Level 14 for confirmed malware (your SOC should be paged). Level 10 for PUA (log it, review weekly). Level 12 for behavioral (these deserve attention but might be false positives, Apex One's ML isn't perfect). Adjust the thresholds to match your team's alert tolerance. If you're getting 50 behavioral alerts a day, bump it to level 8 until you tune the Apex One policy.
Deep Security → Wazuh
Step 1: Enable SIEM Forwarding on Deep Security Manager
Administration → System Settings → SIEM. Check Forward System Events to a remote computer via syslog. Select CEF as the format. Target: your Wazuh manager IP on UDP 514.
Under event categories, enable at minimum:
- Anti-Malware: Real-time and scheduled scan detections on servers. If your domain controller gets hit, this is how you know.
- Intrusion Prevention (IPS): Network-level exploit blocks. SQL injection attempts against your web servers. SMB exploits. Correlate these with the firewall's threat logs and Wazuh's own web server logs.
- Integrity Monitoring: File changes on monitored directories.
/etc/shadowmodified?C:\Windows\System32\drivers\got a new file? This catches persistence mechanisms and privilege escalation artifacts. Deep Security sends the full path and hash, you can correlate the hash with VirusTotal through the integration described in the Wazuh & VirusTotal guide. - Log Inspection: Windows Event Log forwarding from Deep Security agents. Failed logins, privilege use, audit policy changes. These are the same events an agent would collect natively, but forwarded through Deep Security's agent instead of reaching Wazuh directly. Useful if Deep Security agents are already deployed and you want to avoid deploying a separate Wazuh agent on the same box.
- Web Reputation: Blocked HTTP/HTTPS requests. Same concept as Apex One's Web Reputation but on the server side.
CEF:0|Trend Micro|Deep Security|20.0|6000001|IPS Blocked|8|src=10.0.2.15 spt=54321 dst=10.0.2.100 dpt=443
Step 2: Decoders
<decoder name="trendmicro-ds">
<program_name>Trend Micro Deep Security</program_name>
</decoder>
<decoder name="trendmicro-ds-detail">
<parent>trendmicro-ds</parent>
<regex type="pcre2">\|Trend Micro\|Deep Security\|([^\|]+)\|([^\|]+)\|(\d+)\|</regex>
<order>event_type,event_description,severity</order>
</decoder>
<decoder name="trendmicro-ds-source">
<parent>trendmicro-ds</parent>
<regex type="pcre2">src=(\S+)\s+spt=(\d+)\s+dst=(\S+)\s+dpt=(\d+)</regex>
<order>srcip,srcport,dstip,dstport</order>
</decoder>
Step 3: Alert Rules
<rule id="100060" level="14">
<decoded_as>trendmicro-ds</decoded_as>
<field name="event_type" type="pcre2">Anti-Malware|IPS</field>
<description>Trend Micro Deep Security: $(event_type) alert — $(event_description)</description>
<group>trendmicro,deep_security,</group>
</rule>
<rule id="100061" level="12">
<decoded_as>trendmicro-ds</decoded_as>
<field name="event_type" type="pcre2">Integrity Monitoring</field>
<description>Trend Micro Deep Security: File integrity change on $(dstip)</description>
<group>trendmicro,file_integrity,</group>
</rule>
<rule id="100062" level="10">
<decoded_as>trendmicro-ds</decoded_as>
<field name="event_type" type="pcre2">Log Inspection</field>
<description>Trend Micro Deep Security: Log inspection alert — $(event_description)</description>
<group>trendmicro,log_inspection,</group>
</rule>
<rule id="100063" level="8">
<decoded_as>trendmicro-ds</decoded_as>
<field name="event_type" type="pcre2">Web Reputation</field>
<description>Trend Micro Deep Security: Malicious URL blocked on $(dstip)</description>
<group>trendmicro,web_reputation,</group>
</rule>
Why These Two Matter Together
The power isn't in either product alone. It's when they fire together in Wazuh.
Scenario: Apex One detects a suspicious PowerShell invocation via behavioral monitoring on a workstation at 03:14. Two minutes later, Deep Security on the domain controller logs a file integrity change in C:\Windows\SYSVOL\. In isolation, both events are moderate-severity. Together, they're a lateral movement with GPO tampering. Wazuh's correlation engine sees both within the same time window and escalates.
Without this integration, your SOC sees the Apex One alert in one console and the Deep Security alert in another, and the connection between them is invisible.
Troubleshooting: What Breaks and How to Fix It
Check three things, in order: (1) Is <remote><connection>syslog</connection><port>514</port><protocol>udp</protocol></remote> in ossec.conf? (2) Is UDP 514 open in the firewall between Trend Micro and the Wazuh manager? (3) Run tcpdump -i any udp port 514 on the Wazuh manager, do you see packets arriving? If packets arrive but no alerts appear, the decoder regex isn't matching. Check /var/ossec/logs/ossec.log for decoder errors.
Trend Micro occasionally changes the CEF format between versions. The Apex One 14.0 CEF output might differ slightly from 12.0. Run wazuh-logtest on the manager, paste a raw event, and see which decoder catches it. If nothing catches, your program_name regex is wrong, check the raw log for the exact prefix string.
Deep Security's integrity monitoring fires on every file change in monitored directories. If you point it at a web server's /var/www, every deployment creates hundreds of events. Tune the Deep Security policy to exclude deployment directories, or set Wazuh rule 100061 to a lower level and suppress it during known maintenance windows.
What You Get When This Works
Once both products feed into Wazuh, the correlation payoff is immediate. An Apex One malware detection on a workstation combined with a Deep Security IPS alert on a server that workstation was talking to? That's lateral movement. An Apex One behavioral alert plus a Deep Security integrity change on the same host within five minutes? That's a compromise with persistence.
One thing I learned the hard way: Trend Micro's CEF implementation occasionally omits fields that are empty. A malware detection with no filePath is valid, it means the detection was behavioral, not signature-based. Your decoder's regex has to handle missing optional fields. If you use greedy regex patterns ((\S+)), a missing field can cause the decoder to silently skip the entire event. Test with wazuh-logtest using both complete and incomplete sample events before deploying to production.
Correlation: The Part That Makes All Three Worth It
Individual firewall logs are useful. Correlated firewall + Wazuh auth alerts are transformative. This composite rule fires when the same IP triggers a firewall block AND a brute force detection within 60 seconds. That's not a false positive. Someone is actively probing your perimeter and Wazuh caught them on both sides.
<rule id="100099" level="15">
<if_matched_sid>100080,100090,100095</if_matched_sid>
<if_matched_sid>5710,100010,100011</if_matched_sid>
<same_source_ip />
<timeframe>60</timeframe>
<description>CORRELATION: Brute force from $(srcip) confirmed by firewall blocks</description>
<group>correlation,confirmed_threat,</group>
</rule>
Once correlation fires consistently and you trust it, the next step is automated response — having Wazuh trigger a firewall block automatically via Shuffle. The SOC platform guide covers that pipeline end-to-end.