← Back

Wazuh & Firewalls: Unified Logging for Enterprise Defense

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

Network Flow, Firewall Logs → Wazuh
              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.

XML, /var/ossec/etc/decoders/paloalto_decoder.xml
<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>
XML, local_rules.xml
<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.

FortiGate CLI
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
XML, /var/ossec/etc/decoders/fortigate_decoder.xml
<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>
XML, local_rules.xml
<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.

XML, /var/ossec/etc/decoders/pfsense_decoder.xml
<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>
XML, local_rules.xml
<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:

The CEF header Apex One generates looks like this:

Sample Apex One CEF Event
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.

XML, /var/ossec/etc/decoders/trendmicro_apex_decoder.xml
<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

XML, local_rules.xml
<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:

Sample Deep Security CEF Event
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

XML, /var/ossec/etc/decoders/trendmicro_ds_decoder.xml
<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

XML, local_rules.xml
<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

Issue #1: Wazuh isn't receiving logs

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.

Issue #2: Events arrive but decoders don't match

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.

Issue #3: Alert storm on Integrity Monitoring

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.

XML, Composite Correlation Rule
<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.