Catalyst 3850: send syslog to a SIEM instead of polling SNMP

In short. logging host <siem> transport udp port 514, logging trap informational, logging source-interface Vlan1, millisecond timestamps, and archive with log config so every configuration line is a syslog message. On the Wazuh side, a remote syslog listener, the stock Cisco IOS decoder, and one local rule for the events you want to be woken up for. Verify by flapping a spare port and watching the alert arrive.

I want my Catalyst switches in the SIEM without SNMP polling

SNMP polling tells you a counter went up, thirty seconds later, if you built the graph. Syslog tells you the port went down, when, and what else happened in the same second. For a two-member stack per site the events that matter are a short list: link state, LACP bundling, logins, and configuration changes. All four are syslog messages already; the switch just has to be told where to send them. The decision to use syslog rather than SNMP polling is in the decision post at the end.

What you need

  1. A WS-C3850-12X48U stack on IOS-XE 16.12 with a management SVI on VLAN 1.
  2. A Wazuh manager reachable from that SVI. In the examples it is at 10.1.10.30 on the OPS-INT VLAN, which the firewall allows from management.
  3. NTP working on the switch, because an event with the wrong time is worse than no event. show ntp status should say synchronised before you start.
  4. A spare access port on the stack to flap for the test.
flowchart LR
  SW["sw-a.site.example<br/>source Vlan1"] -- "UDP 514, informational" --> WZ["Wazuh manager 10.1.10.30<br/>remote syslog listener"]
  WZ --> DEC["cisco-ios decoder"]
  DEC --> R["Local rules:<br/>LINK, LINEPROTO, EC, SEC_LOGIN, PARSER"]
  R --> AL["Alert"]

Steps

  1. Fix the timestamps first, or every message that follows is dated by uptime:
sw-a.site.example# configure terminal
sw-a.site.example(config)# service timestamps log datetime msec localtime show-timezone year
sw-a.site.example(config)# service timestamps debug datetime msec localtime show-timezone year
sw-a.site.example(config)# clock timezone AEST 10 0
sw-a.site.example(config)# ntp server 10.1.10.1
  1. Point logging at the SIEM, from the management SVI, at informational level. The source interface matters: without it the switch picks whichever interface the route leaves by, and the SIEM's allow-list and hostname mapping break when that changes. origin-id hostname puts the FQDN in every message so the SIEM does not have to guess from the source address.
sw-a.site.example(config)# logging host 10.1.10.30 transport udp port 514
sw-a.site.example(config)# logging source-interface Vlan1
sw-a.site.example(config)# logging trap informational
sw-a.site.example(config)# logging origin-id hostname
sw-a.site.example(config)# logging buffered 65536 informational

Informational is the level at which %LINEPROTO-5-UPDOWN and the LACP messages appear. Notifications (level 5) would keep those but lose some useful level 6 messages; debugging (level 7) is noise.

  1. Log logins, both outcomes:
sw-a.site.example(config)# login on-failure log
sw-a.site.example(config)# login on-success log
  1. Log every configuration command as a syslog message. This is the archive feature; without notify syslog the log stays on the switch where nobody reads it. hidekeys keeps passwords typed into config out of the SIEM.
sw-a.site.example(config)# archive
sw-a.site.example(config-archive)# log config
sw-a.site.example(config-archive-log-cfg)# logging enable
sw-a.site.example(config-archive-log-cfg)# logging size 500
sw-a.site.example(config-archive-log-cfg)# notify syslog contenttype plaintext
sw-a.site.example(config-archive-log-cfg)# hidekeys
sw-a.site.example(config-archive-log-cfg)# end
sw-a.site.example# write memory
  1. On the Wazuh manager, enable the remote syslog listener and restrict it to the management network. This goes in /var/ossec/etc/ossec.conf inside <ossec_config>:
<remote>
  <connection>syslog</connection>
  <port>514</port>
  <protocol>udp</protocol>
  <allowed-ips>10.1.1.0/24</allowed-ips>
  <local_ip>10.1.10.30</local_ip>
</remote>

Wazuh ships a decoder named cisco-ios that understands the %FACILITY-SEVERITY-MNEMONIC: format, and a base set of Cisco IOS rules. You do not write a decoder; you add rules on top of it.

  1. Add a local rule for the events you want to see as alerts. One rule per mnemonic group is enough; put them in /var/ossec/etc/rules/local_rules.xml:
<group name="cisco-ios,local,">
  <rule id="100300" level="7">
    <decoded_as>cisco-ios</decoded_as>
    <match>%LINK-3-UPDOWN|%LINEPROTO-5-UPDOWN</match>
    <description>Cisco IOS: port state change</description>
  </rule>
  <rule id="100301" level="9">
    <decoded_as>cisco-ios</decoded_as>
    <match>%EC-5-UNBUNDLE|%EC-5-BUNDLE|%EC-5-CANNOT_BUNDLE</match>
    <description>Cisco IOS: LACP bundle change</description>
  </rule>
  <rule id="100302" level="10">
    <decoded_as>cisco-ios</decoded_as>
    <match>%SEC_LOGIN-4-LOGIN_FAILED</match>
    <description>Cisco IOS: login failed</description>
  </rule>
  <rule id="100303" level="8">
    <decoded_as>cisco-ios</decoded_as>
    <match>%PARSER-5-CFGLOG_LOGGEDCMD|%SYS-5-CONFIG_I</match>
    <description>Cisco IOS: configuration changed</description>
  </rule>
</group>

Then restart the manager with systemctl restart wazuh-manager and check /var/ossec/logs/ossec.log for a rule syntax complaint.

Verify it worked

Pick the spare port and flap it from the switch:

sw-a.site.example# configure terminal
sw-a.site.example(config)# interface GigabitEthernet1/0/47
sw-a.site.example(config-if)# shutdown
sw-a.site.example(config-if)# no shutdown
sw-a.site.example(config-if)# end

show logging on the switch should end with the %LINK-5-CHANGED, %LINEPROTO-5-UPDOWN and %PARSER-5-CFGLOG_LOGGEDCMD lines for what you just did, with a millisecond timestamp and the right year. On the manager, the same lines should appear in /var/ossec/logs/alerts/alerts.log within a second or two, tagged with rules 100300 and 100303 and the switch's FQDN as the hostname. If the raw line reaches the manager but no alert fires, paste it into /var/ossec/bin/wazuh-logtest; it shows which decoder matched and which rule, or that none did.

Then log in once with a wrong password from the jumphost and confirm rule 100302 fires. Then stop, because the next thing it fires on should be real.

Gotchas

FAQ

Why not SNMP traps instead of polling? Traps are a reasonable middle ground and the 3850 can send them. They need the MIB, a community or SNMPv3 user, and a trap receiver that understands them. Syslog needs none of that and carries the same link and login events as text the SIEM already decodes.

Should I send to two managers? logging host can be repeated for a second destination and the switch sends to both. If the second site's manager is reachable through the mesh, sending each stack to both managers means a site outage does not take its own logs with it.

What level should the alerts be? Level 7 for port state at a site where nothing should move, level 9 for LACP, and 10 for failed logins, map onto Wazuh's default notification threshold of 10 so that only the login failures page someone. Port flaps go in the daily summary. Tune after a month of seeing what is normal, not before.

Related