Monitoring an IPSec tunnel in LM?


Good morning everyone. I've been searching for ways to monitor an IPSec tunnel in LM, but so far I've been unable to find any articles on how to do this. We have a Meraki security device. The Meraki out-of-the-box LM modules do not come with a way to monitor S2S. It would greatly help out our IT team if we could receive a notification on whether or not one of our tunnels is misbehaving in LM. 


14 replies

First question is whether or not you've found something in the Exchange that fits the bill. Someone may have already built and published something.

Second question is, "How would you do this manually today?" If the data exists today, how do you get at it? Is there a web UI? If so, is there a corresponding API endpoint? Is the device hosting that API already added to LogicMonitor? Can you think of a process to repeatedly obtain that data every few minutes? If so, the only thing that would remain would be to script it out in Groovy (or your language of choice) and add it as a scripted DataSource.

Userlevel 1
Badge +3


Good morning everyone. I've been searching for ways to monitor an IPSec tunnel in LM, but so far I've been unable to find any articles on how to do this. We have a Meraki security device. The Meraki out-of-the-box LM modules do not come with a way to monitor S2S. It would greatly help out our IT team if we could receive a notification on whether or not one of our tunnels is misbehaving in LM. 

@SmokinIndo please feel free to DM me if this is something that still interests you.  We are actively testing new modules for Meraki MX that include site-to-site VPN tunnel monitoring (state, tx/rx, packet loss, latency, jitter, MOS...). and would love to get early feedback.

I want IPSEC monitoring for VMware NSX. Is that on PD’s roadmap?

Userlevel 1
Badge +3

Hi, @Mark Hensler. IPSec VPN monitoring for NSX-T is not in my 2023 roadmap. However, certifying support for vSphere 8 is, so if you can share more details about your challenge perhaps we could address it.

I don’t think I have a unique challenge: I would like to refer to a LogicMonitor graph to check the health of an IPSEC VPN at the time of a reported connectivity issue. The only thing special about my case is that we use VMware NSX-T as our endpoint.

Userlevel 1
Badge +3

Thanks @Mark Hensler, I am looking into this.  Would these be the metrics you’d be interested in?

Tunnel status, speed,  transmit Bps, receive Bps, throughput Bps, Utilization Percentage, Packet Loss Percentage,  Jitter, Latency, Mean Opinion Score

 

Any others?

 

 

I have not checked the API for NSX-T to know what is available, but yes, those are great metrics to track.

There is value to recording the “status” of both phase 1 and phase 2 separately. Sometimes, phase 1 will remain up while phase 2 goes down. This can indicate a lack of activity within the scope of that phase 2 rather than a broken link between the sites.

When I built this for NSX-V (unpublished DataSource named “Jenzabar_VMware_NSX_EdgeIPSEC”), the excerpt below created the instances for phase 1 and phase 2. Note the different naming convention as well as the populated properties.

edges.each { edge ->
def confURL = "https://${hostname}/api/4.0/edges/${edge.id}/ipsec/config"

def confResponse = getURL(confURL, slurper, auth, debug)
def statusCode2 = confResponse[1]

if (statusCode2 == 200) {
def confOutput = confResponse[0].sites.sites
confOutput.each { conf ->
LMDebugPrint("conf: \n" + JsonOutput.prettyPrint(JsonOutput.toJson(conf)), debug)

// Phase 1
println "${edge.id}_${conf.siteId}##${conf.name} (Phase 1)######"+
"auto.nsx.edge=${edge.name} [${edge.id}]"+
"&auto.nsx.edgeId=${edge.id}"+
"&auto.nsx.ipsec.localIp=${conf.localIp}"+
"&auto.nsx.ipsec.siteId=${conf.siteId}"+
"&auto.nsx.ipsec.peerIp=${conf.peerIp}"

// Phase 2
conf.localSubnets.subnets.each { local ->
conf.peerSubnets.subnets.each { peer ->
tunnelKey = "${conf.localIp}_${local}-${conf.peerIp}_${peer}"
println "${tunnelKey}##${conf.name} (${tunnelKey})######"+
"auto.nsx.edge=${edge.name} [${edge.id}]"+
"&auto.nsx.edgeId=${edge.id}"+
"&auto.nsx.ipsec.localIp=${conf.localIp}"+
"&auto.nsx.ipsec.siteId=${conf.siteId}"+
"&auto.nsx.ipsec.peerIp=${conf.peerIp}"
}
}

}
}
}

 

The API for NSX-V also included a failureMessage. I created an enumeration metric (is this a proper term?) for which I started to translate common failureMessage values into numeric values (see excerpt below).

failureMessage = 0
if (tunnel.containsKey("failureMessage") && tunnel.failureMessage.length()) {
switch (tunnel.failureMessage.toLowerCase()) {
case "received delete for ipsec-sa":
failureMessage = 3
break
case "unknown":
failureMessage = 2
break
default:
failureMessage = 1
}
}
println "${tunnelKey}.failureMessage="+failureMessage

 

Userlevel 1
Badge +3

Out of curiosity to see how smart or dumb AI is (and perhaps for a little fun), I asked https://chat.openai.com (ChatGPT) the following:

Using Groovy 2.4, Write a multi-instance LogicMonitor DataSource for VMware NSX-T that exposes each IPsec tunnel as a discrete DataSource instance with the following datapoints:  tunnel status, tunnel speed, tunnel  transmit Bps, tunnel receive Bps, Tunnel throughput Bps, Tunnel Utilization Percentage, Tunnel Packet Loss Percentage, Tunnel Jitter, Tunnel Latency, Tunnel Mean Opinion Score

This was the response:

​​​​​​Here's an example of a Groovy 2.4 script for a multi-instance LogicMonitor DataSource for VMware NSX-T that exposes each IPsec tunnel as a discrete DataSource instance with the specified datapoints:
​​​​​​

import com.santaba.agent.groovyapi.datasources.*
import com.santaba.agent.groovyapi.execute.*

// Define the credentials to access the NSX-T API
def nsxHost = "nsx-t-hostname"
def nsxUsername = "username"
def nsxPassword = "password"

// Define the API endpoint to retrieve the IPsec tunnels
def endpoint = "/api/v1/ipsec-tunnel"

// Initialize the LogicMonitor DataSource
def ds = new BasicDataSource("NSX-T IPsec Tunnels")

// Set the custom properties for the DataSource
ds.setMultiInstance(true)
ds.addCustomProperty("nsxHost", nsxHost)
ds.addCustomProperty("nsxUsername", nsxUsername)
ds.addCustomProperty("nsxPassword", nsxPassword)

// Define the datapoints to retrieve for each IPsec tunnel
def datapoints = [
    "status",
    "speed",
    "txBps",
    "rxBps",
    "throughputBps",
    "utilizationPercent",
    "packetLossPercent",
    "jitter",
    "latency",
    "meanOpinionScore"
]

// Loop through each IPsec tunnel and create a DataSource instance
executeWithApi("nsxt", { api ->
    def tunnels = api.get(endpoint)
    tunnels.each { tunnel ->
        def instanceName = "Tunnel " + tunnel.id
        def instance = ds.addInstance(instanceName)

        // Set the instance custom properties
        instance.addCustomProperty("tunnelId", tunnel.id)

        // Add the datapoints to the instance
        datapoints.each { datapoint ->
            instance.addDatapoint(new SimpleDatapoint(datapoint))
        }
    }
})

// Save the DataSource
ds.save()
 

What?!?! 😮

Does that code work?

Userlevel 6
Badge +14

LM requires one script for discovery and one for collection. However, this gets you close. The output format needs to be adjusted. It’s interesting that it knew about the santaba libraries but did not use hostProps.get() method calls. 

Userlevel 1
Badge +3

I wonder if we can teach it by interacting with it?

Userlevel 1
Badge +3

@Stuart Weenig, I rephrased my question as:

​​​​​​Using Groovy 2.4, write the discovery and collection scripts for a multi-instance LogicMonitor DataSource for VMware NSX-T that exposes each IPsec tunnel as a discrete DataSource instance with the following datapoints:  tunnel status, tunnel speed, tunnel  transmit Bps, tunnel receive Bps, Tunnel throughput Bps, Tunnel Utilization Percentage, Tunnel Packet Loss Percentage, Tunnel Jitter, Tunnel Latency, Tunnel Mean Opinion Score.

Below is the response:
​​​

Here's an example of a Groovy 2.4 script for the discovery and collection of a multi-instance LogicMonitor DataSource for VMware NSX-T that exposes each IPsec tunnel as a discrete DataSource instance with the specified datapoints:

Discovery script:
​​​​​​import com.santaba.agent.groovyapi.datasources.*
import com.santaba.agent.groovyapi.execute.*

// Define the credentials to access the NSX-T API
def nsxHost = "${nsxHost}"
def nsxUsername = "${nsxUsername}"
def nsxPassword = "${nsxPassword}"

// Define the API endpoint to retrieve the IPsec tunnels
def endpoint = "/api/v1/ipsec-tunnel"

// Define the datapoints to retrieve for each IPsec tunnel
def datapoints = [
    "status",
    "speed",
    "txBps",
    "rxBps",
    "throughputBps",
    "utilizationPercent",
    "packetLossPercent",
    "jitter",
    "latency",
    "meanOpinionScore"
]

// Initialize the LogicMonitor DataSource
def ds = new BasicDataSource("NSX-T IPsec Tunnels")

// Set the custom properties for the DataSource
ds.setMultiInstance(true)
ds.addCustomProperty("nsxHost", nsxHost)
ds.addCustomProperty("nsxUsername", nsxUsername)
ds.addCustomProperty("nsxPassword", nsxPassword)

// Retrieve the list of IPsec tunnels and create a DataSource instance for each tunnel
executeWithApi("nsxt", { api ->
    def tunnels = api.get(endpoint)
    tunnels.each { tunnel ->
        def instanceName = "Tunnel " + tunnel.id
        def instance = ds.addInstance(instanceName)

        // Set the instance custom properties
        instance.addCustomProperty("tunnelId", tunnel.id)

        // Add the datapoints to the instance
        datapoints.each { datapoint ->
            instance.addDatapoint(new SimpleDatapoint(datapoint))
        }
    }
})

// Save the DataSource
ds.save()
 

Collection Script:
​​​​​​

import com.santaba.agent.groovyapi.datasources.*
import com.santaba.agent.groovyapi.execute.*
import com.santaba.agent.groovyapi.http.*

// Retrieve the custom properties for the DataSource
def nsxHost = "${nsxHost}"
def nsxUsername = "${nsxUsername}"
def nsxPassword = "${nsxPassword}"

// Retrieve the API endpoint for the specified IPsec tunnel
def getEndpoint = { tunnelId ->
    "/api/v1/ipsec-tunnel/${tunnelId}"
}

// Retrieve the datapoint values for the specified IPsec tunnel
def getData = { tunnelId ->
    def datapoints = [
        "status",
        "speed",
        "txBps",
        "rxBps",
        "throughputBps",
        "utilizationPercent",
        "packetLossPercent",
        "jitter",
        "latency",
        "meanOpinionScore"
    ]

    def values = [:]

    executeWithApi("nsxt", { api ->
        def endpoint = getEndpoint(tunnelId)
        def tunnel = api.get(endpoint)

        datapoints.each { datapoint ->
            values[datapoint] = tunnel[datapoint]
        }
    })

    values
}

// Loop through each IPsec tunnel and collect its datapoints
executeWithApi("nsxt", { api ->
    def tunnels = api.get("/api/v1/ipsec-tunnel")
    tunnels.each { tunnel ->
        def instanceName = "Tunnel " + tunnel.id
        def instance = findDataSourceInstance(dsName, instanceName)

        if (instance != null
 

Userlevel 6
Badge +14

Meh, the discovery script looks like it’s trying to create the datasource, not create the instance output required for the discovery. 

The collection script seems incomplete. @Patrick Rouse , try embedding the code using the code widget. Will make it easier to read. 

Since the santaba libraries are not documented (except for self documentation), it would be difficult to know exactly what it’s trying to save with the ds.Save() statement. 

Userlevel 1
Badge +3

It’s trying to look smarter than it is.

Reply