What is this?

This knowledgebase contains questions and answers about PRTG Network Monitor and network monitoring in general.

Learn more

PRTG Network Monitor

Intuitive to Use. Easy to manage.
More than 500,000 users rely on Paessler PRTG every day. Find out how you can reduce cost, increase QoS and ease planning, as well.

Free Download

Top Tags


View all Tags

Translate sensor message into something triggerable

Votes:

0

Hi,

i am trying to implement IPSec VPN monitoring for our Palo Alto Firewall. At the moment we operate 28 branch sites and counting. I was able to obtain an XML file using Palo Altos API (https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClWUCA0) and pick my information out of the nodes using the HTTP XML/REST Value sensor. I am crawling through the results via the XML-Node option IPSec/entry[1]/state to IPSec/entry[n]/state.

There are three states: active, inactive, init

I now receive a sensor message like IPSec/entry[1]/state holds value active or IPSec/entry[3]/state holds value init.

Here comes my question:

I want to somehow parse the message information into something useful in order to trigger notification if the value is not active.

Is there anyone who can help me out?

PS: I tried setting the monitoring up using this awesome Powershell script (http://www.hospitableit.com/howto/monitoring-an-ipsec-tunnel-on-a-palo-alto-firewall-using-prtg/) but i'm stuck there with some encryption/decryption errors....so the above is my workaround.

Thanks a lot in advance for any answer!

Best regards,

Robert

http-xml-rest-value-sensor palo-alto xml

Created on Feb 28, 2019 3:00:39 PM



10 Replies

Votes:

1

Hello Robert,

This should be possible with the newer REST Custom Sensor, but is not a simple process with just a few clicks. You need to have a template file in place that contains definitions for the data you actually want to extract and contains information about the possible return values, so it can "translate" them accordingly.

In case you don't come along with the documentation, please send us the full output you get when querying the API, so we can check how the template needs to look like.

You can use the sensor now already with template "Channel Discovery", just to check if you can it to connect properly to the firewall in order to retrieve the information.

Kind regards,

Erhard

Created on Mar 1, 2019 1:36:46 PM by  Erhard Mikulik [Paessler Support]



Votes:

0

Hey Erhard,

thanks a lot for your reply. I was able to get results when using the REST API sensor but only for a quite simple query. My problem is that i need this Query:

/api/?type=op&cmd=<show><running><tunnel><flow><all></all></flow></tunnel></running></show>&key=MYAPIKEY

but all < or > signs will be replaced into { or } signs.

So after i hit the Save the query will look like this and my server won't be responding to that:

/api/?type=op&cmd={show}{running}{tunnel}{flow}{all}{/all}{/flow}{/tunnel}{/running}{/show}&key=MYAPIKEY

Any guesses?

Best regards Robert

Created on Mar 4, 2019 8:26:38 AM

Last change on Mar 4, 2019 8:26:38 AM



Votes:

0

Hi Robert,

That is intentional in order to avoid injecting malicious code using such fields in PRTG, so it replaces "<>" with "{}" in most fields. I need to address this internally and post an update here as soon as I know more.

Kind regards,

Erhard

Created on Mar 4, 2019 8:49:15 AM by  Erhard Mikulik [Paessler Support]

Last change on Mar 4, 2019 8:49:35 AM by  Erhard Mikulik [Paessler Support]



Votes:

0

Ok, here's a workaround for now:

The sensor uses "rest.exe", you find it in PRTG's installation path in folder "Sensor System". Open a command prompt there and run the exe without any parameters, it will show the parameters it expects.

Without any additional parameters, this is how it can be executed from command prompt: rest.exe "https://targethost/api/?type=op&cmd=<show><running><tunnel><flow><all></all></flow></tunnel></running></show>&key=MYAPIKEY "path_to_template"

"path_to_template" means you enter the data-path where the used template file is located, those are located in PRTG's installation path as well in "Custom Sensors/rest", so it might look like this: "C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\rest\my-file.template"

If you can get it to work from command prompt, you can copy rest.exe to "Custom Sensors\EXEXML" and use an EXE/Script Advanced Sensor where you select "rest.exe" then and pass along the parameters as in command prompt in field "Parameters" of the sensor.

Kind regards,

Erhard

Created on Mar 4, 2019 9:56:03 AM by  Erhard Mikulik [Paessler Support]



Votes:

0

So, i got it working from command line with:

rest.exe "https://paloaltofirewallfqdn/api/?type=op&cmd=<show><running><tunnel><flow><all></all></flow></tunnel></running></show>&key=MYAPIKEY" "C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\rest\paloalto.vpnstatus.template" -tlsignore

I created the paloalto.vpnstatus.template myself with the following content in order to test the result for the 1st entry:

{
  "prtg": {
    "description" : {
      "device": "DEVICE",
      "query": "/api/?type=op&cmd=<show><running><tunnel><flow><all></all></flow></tunnel></running></show>&key=MYAPIKEY",
      "comment": "COMMENT"
    },
    "result": [
      {
        "channel": "VPN Status" ,
        "value": $.IPSec.entry[1].state
      }
    ]
  }
}

The response in command line is: {"prtg": {"Error": "1","Text": "Could no evaluate channel value of VPN Status: unknown key IPSec."}

I'm pretty sure that i did something wrong but as I'm absolutely new to REST API / XML stuff, I'm happy for any advice on this.

The Palo Alto XML Output looks like this:

<response status="success">
   <result>
      <total>31</total>
      <SSL-VPN/>
      <GlobalProtect-site-to-site/>
      <hop/>
      <num_ipsec>30</num_ipsec>
      <num_sslvpn>1</num_sslvpn>
   <GlobalProtect-Gateway>...</GlobalProtect-Gateway>
      <dp>dp0</dp>
   <IPSec>
      <entry>
            <peerip>XXX.XXX.XXX.XXX</peerip>
            <name>xxxxxxxxxxx</name>
            <outer-if>ethernet1/4</outer-if>
            <gwid>1</gwid>
            <localip>XXX.XXX.XXX.XXX</localip>
            <state>active</state>
            <inner-if>tunnel.1</inner-if>
            <mon>off</mon>
            <owner>1</owner>
            <id>1</id>
         </entry>
         <entry>
            <peerip>XXX.XXX.XXX.XXX</peerip>
            <name>xxxxxxxxxxx</name>
            <outer-if>ethernet1/4</outer-if>
            <gwid>4</gwid>
            <localip>XXX.XXX.XXX.XXX</localip>
            <state>active</state>
            <inner-if>tunnel.134</inner-if>
            <mon>off</mon>
            <owner>1</owner>
            <id>7</id>
         </entry>
         <entry>
            <peerip>0.0.0.0</peerip>
            <name>xxxxxxxxxxx</name>
            <outer-if>ethernet1/4</outer-if>
            <gwid>7</gwid>
            <localip>XXX.XXX.XXX.XXX</localip>
            <state>init</state>
            <inner-if>tunnel.101</inner-if>
            <mon>off</mon>
            <owner>1</owner>
            <id>8</id>
         </entry>
         <entry>
            <peerip>0.0.0.0</peerip>
            <name>xxxxxxxxxxx</name>
            <outer-if>ethernet1/4</outer-if>
            <gwid>8</gwid>
            <localip>XXX.XXX.XXX.XXX</localip>
            <state>init</state>
            <inner-if>tunnel.104</inner-if>
            <mon>off</mon>
            <owner>1</owner>
            <id>9</id>
         </entry>
         <entry>
            <peerip>0.0.0.0</peerip>
            <name>xxxxxxxxxxx</name>
            <outer-if>ethernet1/4</outer-if>
            <gwid>9</gwid>
            <localip>XXX.XXX.XXX.XXX</localip>
            <state>init</state>
            <inner-if>tunnel.112</inner-if>
            <mon>off</mon>
            <owner>1</owner>
            <id>10</id>
         </entry>
      </IPSec>
   </result>
</response>

Created on Mar 4, 2019 12:02:20 PM

Last change on Mar 4, 2019 12:59:09 PM by  Erhard Mikulik [Paessler Support]



Votes:

0

From what I see and read in between the lines you have PRTG execute the REST command directly.

What you will need to do is to build a script around it - I suggest PowerShell - probably even use PowerShell with Web-Request-Injection commands instead of engaging your rest.exe.

PowerShell can directly interact with a RESTful API. Gather the data and process it.

What you want to end up with is a output that PRTG understands.

What you have right now is a output that Palo Alto provides.

Both is XML based, but thought it is the same language, they speak very different dialects and about different topics so you need someone in the middle to translate all of it - a script.

Don't have a Palo Alto - so I can't fully test this - but this can be done and shouldn't be to complicated. Pretty sure we can get you there...

Regards

Florian Rossmark

www.it-admins.com

Created on Mar 4, 2019 4:05:41 PM



Votes:

0

The template is the problem, we're now at back at the initial problem: How to make PRTG interpret string values like "Active" into something it can work with?

Adjust the template, this one here is assuming that the returned state can be either "active" or "inactive":

{
  "prtg": {
    "description" : {
      "device": "DEVICE",
      "query": "/api/?type=op&cmd=<show><running><tunnel><flow><all></all></flow></tunnel></running></show>&key=MYAPIKEY",
      "comment": "COMMENT"
    },
    "result": [
      {
        "channel": "VPN Status" ,
        "value": { $..IPSep.entry[#1].name: $..IPSec.entry.state.*.(lookup(@, 'active', 'inactive')) },
        "Valuelookup": "prtg.customlookup.ipsec.state"
      }
    ]
  }
}

You need to create now an addtional file named "prtg.customlookup.ipsec.state.ovl" with a text editor that contains this:

<?xml version="1.0" encoding="UTF-8"?>
  <ValueLookup id="prtg.customlookup.ipsec.state" desiredValue="0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="PaeValueLookup.xsd">
    <Lookups>
     <SingleInt state="Ok" value="0">
        Active
      </SingleInt>
      <SingleInt state="Error" value="1">
        Inactive
      </SingleInt>
    </Lookups>
  </ValueLookup>

Save "prtg.customlookup.ipsec.state.ovl" on your PRTG server in PRTG installation path in subfolder "lookups/custom", you might to trigger a reload of lookups and filelists in "Setup | System Administration | Administrative Tools".

Kind regards,

Erhard

Created on Mar 4, 2019 4:22:47 PM by  Erhard Mikulik [Paessler Support]



Votes:

1

Hey Erhard,

i had to modify the template slightly:

{
  "prtg": {
    "description" : {
      "device": "DEVICE",
      "query": "/api/?type=op&cmd=<show><running><tunnel><flow><all></all></flow></tunnel></running></show>&key=MYAPIKEY",
      "comment": "COMMENT"
    },
    "result": [
      {
        "channel": "VPN-Status" ,
        "value": { $..IPSec.entry[*].name: $..IPSec.entry[*].state..(lookup(@, 'active', 'init', 'inactive')) },
		"ValueLookup": "prtg.customlookup.ipsec.state"
      }
    ]
  }
}

I am now getting a proper response in the command line output which looks like this: Screenshot of Output (Sorry, I had to blur some of the contents) So basically it says

{"prtg": {"Error": "1","Text": "Could not create channel VPN-Status[VPN-Name1:Domain VPN-Name2:Domain VPN-NameN:Domain] : expected number but got [0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1] ([]interface {})."}}

0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 represents the correct states of the tunnels because at the moment only tunnel 1 and 2 are up. The rest is down.

It "feels" like the template doesn't use / reference the lookup script correctly. When I delete the ValueLookup part from the template script, it shows exactly the same output...

Thank you so much Erhard, for helping!

Best regards, Robert

Created on Mar 5, 2019 12:48:57 PM



Votes:

2

Got it working by manually counting the number of the entry up.

The template I used:

{
  "prtg": {
    "description" : {
      "device": "DEVICE",
      "query": "/api/?type=op&cmd=<show><running><tunnel><flow><all></all></flow></tunnel></running></show>&key=MYAPIKEY",
      "comment": "COMMENT"
    },
    "result": [
      {
        "channel": $..IPSec.entry[0].name ,
        "value": $..IPSec.entry[0].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[1].name ,
        "value": $..IPSec.entry[1].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[2].name ,
        "value": $..IPSec.entry[2].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[3].name ,
        "value": $..IPSec.entry[3].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[4].name ,
        "value": $..IPSec.entry[4].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[5].name ,
        "value": $..IPSec.entry[5].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[6].name ,
        "value": $..IPSec.entry[6].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[7].name ,
        "value": $..IPSec.entry[7].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[8].name ,
        "value": $..IPSec.entry[8].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[9].name ,
        "value": $..IPSec.entry[9].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[10].name ,
        "value": $..IPSec.entry[10].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[11].name ,
        "value": $..IPSec.entry[11].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[12].name ,
        "value": $..IPSec.entry[12].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[13].name ,
        "value": $..IPSec.entry[13].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[14].name ,
        "value": $..IPSec.entry[14].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[15].name ,
        "value": $..IPSec.entry[15].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[16].name ,
        "value": $..IPSec.entry[16].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[17].name ,
        "value": $..IPSec.entry[17].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[18].name ,
        "value": $..IPSec.entry[18].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[19].name ,
        "value": $..IPSec.entry[19].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[20].name ,
        "value": $..IPSec.entry[20].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[21].name ,
        "value": $..IPSec.entry[21].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[22].name ,
        "value": $..IPSec.entry[22].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[23].name ,
        "value": $..IPSec.entry[23].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[24].name ,
        "value": $..IPSec.entry[24].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[25].name ,
        "value": $..IPSec.entry[25].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[26].name ,
        "value": $..IPSec.entry[26].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[27].name ,
        "value": $..IPSec.entry[27].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[28].name ,
        "value": $..IPSec.entry[28].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },
	  {
        "channel": $..IPSec.entry[29].name ,
        "value": $..IPSec.entry[29].state(lookup(@, 'active', 'init', 'inactive')) ,
		"ValueLookup": "prtg.customlookup.ipsec.state"
      },  
    ]
  }
}

The lookup I used:

<?xml version="1.0" encoding="UTF-8"?>
  <ValueLookup id="prtg.customlookup.ipsec.state" desiredValue="0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="PaeValueLookup.xsd">
    <Lookups>
     <SingleInt state="Ok" value="0">
        Active
      </SingleInt>
      <SingleInt state="Error" value="1">
        Init
      </SingleInt>
       <SingleInt state="Error" value="2">
        Inactive
      </SingleInt>
    </Lookups>
  </ValueLookup>

Sensor settings in EXE/Script Advanced: Exe/script: rest.exe (copied from C:\Program Files (x86)\PRTG Network Monitor\Sensor System) Parameters: "https://paloaltofqdn/api/?type=op&cmd=<show><running><tunnel><flow><all></all></flow></tunnel></running></show>&key=MYAPIKEY" "C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\rest\paloalto.vpnstatus.template" -tlsignore

This is the result: https://ibb.co/bNRDM7p

This sensor is not taking care of the Palo Alto cluster we have, as it's only monitoring the active firewall (but to be honest, if the active firewall is not working anymore, i at first got other problems than VPN states) and it's counting up manually so i have to keep in mind to extend the template when i got more than 30 IPSec VPNs but for now this will be doing it until maybe someone can help improving this.

Thanks a lot again, Erhard for your help!

Created on Mar 5, 2019 1:48:59 PM

Last change on Mar 5, 2019 3:01:04 PM by  Erhard Mikulik [Paessler Support]



Votes:

1

I see you understood how the logic works and did the right adjustments regarding the template and lookup, great work!

Kind regards,

Erhard

Created on Mar 5, 2019 2:04:18 PM by  Erhard Mikulik [Paessler Support]




Disclaimer: The information in the Paessler Knowledge Base comes without warranty of any kind. Use at your own risk. Before applying any instructions please exercise proper system administrator housekeeping. You must make sure that a proper backup of all your data is available.