Cisco Meraki – Collect SNMP trap data with Azure Monitor Agent

Since it’s recommended to have a break-glass admin account in case of SAML authentication failures or other account issues, a security concern arises. Break-glass admin logins might go unnoticed, as there’s no native feature to send login alerts for specific accounts. The solution isn’t straightforward, as Cisco Meraki only allows login attempts to be sent via SNMP traps.

To process the SNMP messages, we’re using a few services:

  • Rad Hat Enterprise Linux VM
  • Azure Monitor Agent
  • Log Analytics Workspace

The message and processing flow are visualized below. Whenever a Meraki Dashboard user (non-SAML) logs in, an SNMP message is sent to the RHEL VM. Since the Azure Monitor Agent cannot natively process SNMP, we configure the trap service to write the message to syslog. The Monitor Agent reads the syslog at intervals and sends the data to a Log Analytics workspace. A KQL query is then used to check for logins within a specified timeframe.

VM to capture SNMP

Go to your Azure Portal and create a new VM, preferably RHEL 8 or 9, as this guide has only been tested on these operating systems. The VM size «B1ms» should be sufficient. Once the VM is created, SSH into it. Use a private IP and access it through another host or VPN; publicly exposing port 22 on your VM is not recommended.

PowerShell
ssh -i path-to-private-key azureuser@ipaddress

Once connected, install the SNMP agent and enable the snmptrapd service:

ShellScript
#Install the SNMP agent
sudo yum install net-snmp
#Enable the service
sudo systemctl enable snmptrapd
#Allow UDP 162 through the firewall
sudo firewall-cmd --zone=public --add-port=162/udp
sudo firewall-cmd --zone=public --add-port=162/udp --permanent
sudo firewall-cmd --reload

Download the Meraki MIB file for SNMP, copy it to the appropriate location, set the correct permissions, and adjust the SELinux context of the file. Without these steps, the snmptrapd service will not be able to read the file.

ShellScript
curl -L -o MERAKI-CLOUD-CONTROLLER-MIB.txt https://n201.meraki.com/resources/MERAKI-CLOUD-CONTROLLER-MIB.mib
sudo mv MERAKI-CLOUD-CONTROLLER-MIB.txt /usr/share/snmp/mibs/
sudo chmod 644 /usr/share/snmp/mibs/MERAKI-CLOUD-CONTROLLER-MIB.txt
sudo semanage fcontext -a -t snmpd_var_lib_t "/usr/share/snmp/mibs/meraki-MIB.txt"
sudo restorecon -v /usr/share/snmp/mibs/MERAKI-CLOUD-CONTROLLER-MIB.txt

To gather the engine ID from the meraki cloud use the following command.

ShellScript
sudo snmptrapd -f -Dlcd_set_enginetime -Lo

Try sending a test message to your trap from the Meraki dashboard. Navigate to Organization -> Login Attempts -> Configure.

Back on your RHEL VM you should see the ID displayed after you send the test trap:

ShellScript
NET-SNMP version 5.8
lcd_set_enginetime: engineID 00 00 00 00 00 00 00 00 00 00 00 : boots=0, time=0

Afterwards, change the snmptrapd.conf file to only allow SNMPv3 and create the user needed to connect.

ShellScript
sudo vi /etc/snmp/snmptrapd.conf

To add content in the file, press «i» to insert. To exit and save, press «ESC» and type «:wq». Change the engine ID, username and password. Be sure to add «0x» in front of the engine ID. Details regarding SNMP trap: TUT:snmptrap SNMPv3 – Net-SNMP Wiki

ShellScript
	# authCommunity log,execute,net public
	createUser -e 0xYOURENGINEIDHERE yourusernamehere SHA yourpasswordhere AES yourpasswordhere
	authUser log,execute,net merakisnmp
	# Format logs for collection by Azure Monitor Agent
	format2 snmptrap %a %B %y/%m/%l %h:%j:%k %N %W %q %T %W %v \n

Configure the trap receiver to send data to syslog.

ShellScript
sudo vi /etc/sysconfig/snmptrapd

Change the file’s content to.

ShellScript
OPTIONS="-m ALL -Ls2"

To get the services status you can use systemctl, if need you can also user start, stop or restart instead of status.

ShellScript
sudo systemctl status snmptrapd

To show a live tail of the snmptrapd log messages, use the following command.

ShellScript
journalctl -u snmptrapd -f

If the service is running and you see no errors, you can start with the azure monitor agent and log analytics workspace deployment.

Configure automatic Alerting

There are two options on how to receive alerts, either use Azure Monitor Alerts or if you already have Azure Sentinel, you can just add an analytics rule. Either way, the same KQL query can be used to filter for sign ins. If you don’t have Azure Sentinel proceed with Processing and collecting logs through LAW otherwise skip this section and head to the next section Processing and collecting logs through Sentinel.

Processing and collecting logs through LAW

Deploy a new log analytics workspace and create a new data collection rule.

Add the VM as a resource.

Create a new endpoint.

Click on the endpoint checkbox and then select the endpoint you’ve just created.

On the next tab, add the datasource, deselect everything except LOG_LOCAL2 with the minimum log level set to LOG_INFO.

Create the data collection rule and test the collection by logging in with a non SAML meraki dashboard user. There should be a new syslog entry in the logs.

Head over to the alerts tab in your log analytics workspace to create a custom alert.

Use the custom log search with the following query to look up logins and parse the SNMP message. Make sure to modify the query to match your USERNAME@DOMAIN.TLD.

Kusto
Syslog
| where SyslogMessage has "USERNAME@DOMAIN.TLD"
| parse  SyslogMessage with *
        "MERAKI-CLOUD-CONTROLLER-MIB::organizationName.0 = STRING: " organizationName: string
        "MERAKI-CLOUD-CONTROLLER-MIB::networkName" *
        "MERAKI-CLOUD-CONTROLLER-MIB::loginEmail = STRING: " loginEmail: string
        "MERAKI-CLOUD-CONTROLLER-MIB::loginResult = STRING: " loginResult: string
        "MERAKI-CLOUD-CONTROLLER-MIB::loginDescription = STRING: " loginDescRaw: string
| extend organizationName = trim("#011", organizationName) 
// Convert the loginDescription string into dynamic JSON
| extend loginJson = parse_json(loginDescRaw)
// Filter only where login is success
| where loginJson.success == true
// Choose what fields to return
| project TimeGenerated,
          organizationName,
          Email         = tostring(loginJson.email),
          IP            = tostring(loginJson.ip),
          Location      = tostring(loginJson.location),
          AuthType      = tostring(loginJson.auth_type),
          LoggedInAt    = tostring(loginJson.logged_in_at),
          Success       = tobool(loginJson.success)

The measurement and alert logic can be configured to your preference. Running this every 15 minutes will cost approximately $0.50 per month.

Finish the custom alert rule by adding an action and specifying the necessary details. Depending on your action you will receive an alert if the break glass admin query has more than one result.

Processing and collecting logs through Sentinel

Open the Sentinel connectors and add «Syslog via AMA». Once added, navigate to the connector page.

Create a new data collection rule.

Select your SNMP VM and collect the LOG_LOCAL2. This is the equivalent of what we’ve configured with OPTIONS=»-m ALL -Ls2″ in the path /etc/sysconfig/snmptrapd.

Test the collection by logging in with a non SAML meraki dashboard user. There should be a new syslog entry in the sentinel logs.

If everything looks correct, head over to the analytic rules and create a new one.

In the rule logic tab use the this query to gather the logins for your break glass admin be sure to change the query regarding your USERNAME@DOMAIN.TLD.

Kusto
Syslog
| where SyslogMessage has "USERNAME@DOMAIN.TLD"
| parse  SyslogMessage with *
        "MERAKI-CLOUD-CONTROLLER-MIB::organizationName.0 = STRING: " organizationName: string
        "MERAKI-CLOUD-CONTROLLER-MIB::networkName" *
        "MERAKI-CLOUD-CONTROLLER-MIB::loginEmail = STRING: " loginEmail: string
        "MERAKI-CLOUD-CONTROLLER-MIB::loginResult = STRING: " loginResult: string
        "MERAKI-CLOUD-CONTROLLER-MIB::loginDescription = STRING: " loginDescRaw: string
| extend organizationName = trim("#011", organizationName) 
// Convert the loginDescription string into dynamic JSON
| extend loginJson = parse_json(loginDescRaw)
// Filter only where login is success
| where loginJson.success == true
// Choose what fields to return
| project TimeGenerated,
          organizationName,
          Email         = tostring(loginJson.email),
          IP            = tostring(loginJson.ip),
          Location      = tostring(loginJson.location),
          AuthType      = tostring(loginJson.auth_type),
          LoggedInAt    = tostring(loginJson.logged_in_at),
          Success       = tobool(loginJson.success)

If everything is configured correctly, you should get an alert based on your analytics rule.

References

Collect SNMP trap data with Azure Monitor Agent – Azure Monitor | Microsoft Learn

SNMP Overview and Configuration – Cisco Meraki Documentation

Kommentare

Schreiben Sie einen Kommentar

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert