Skip to main content

Is often necessary to audit the same setting across multiple vendors.

For example, the NTP server setting differs across vendors.

You can write an exportable function that returns the NTP servers for ANY device type.

The following NQE query creates a reusable function called getNtpServers with the export command. The function takes a single argument device and returns a list of NTP servers based on the device type. A different function is called using the when statement based on the OS. 

Note that the NTP server configuration in Cisco devices is in the device.files.config file (the running configuration), but for Fortinet and F5 devices you need the result of a Custom Command to see the NTP configuration.

Note also that the same nested function getCiscoNtpServers is used for multiple Cisco OS types.

If you find an OS type or pattern that isn’t identified, you can add in new patterns and functions, or alter or expand the patterns already included.

/**
* @intent Helper function getNtpServers(device: Device) returns a list of NTP servers for a given device.
* @description Helper function getNtpServers(device: Device) returns a list of NTP servers for a given device.
*
* NOTE - A custom command must be added for the function to work for the following device types:
*
* Fortinet - show system ntp
* F5 - list sys ntp
*/

patternFortinet =
```
config system ntp
config ntpserver
edit {number}
set server {ntpServer:string}
```;

patternPaloAlto =
```
config
devices
localhost.localdomain
deviceconfig
system
ntp-servers
primary-ntp-server
ntp-server-address {primaryNtpServer:string}
secondary-ntp-server
ntp-server-address {secondaryNtpServer:string}
```;

patternCisco = ```
ntp server
```;

patternIosXr =
```
ntp
server vrf {vrf:string} {ntpServer:string} {string*}
```;

patternArista = ```
ntp server
```;

patternF5 = ```
sys ntp
servers {ntpServer:(string*)}
```;

patternJunos = ```
system
ntp
server {ntpServer:string}
```;

patternDell = ```
sntp server {ntpServer:string}
```;

patternDell2 = ```
ntp server {ntpServer:string}
```;

patternVersa = ```
ntp
server {ntpServer:string}
```;

getFortinetNtpServers(device) =
foreach command in device.outputs.commands
where command.commandText == "show system ntp"
let commandString = command.response
let editedCommandString = replace(commandString, "\"", "")
let parsedCommand = parseConfigBlocks(OS.FORTINET, editedCommandString)
foreach match in blockMatches(parsedCommand, patternFortinet)
select { serverId: match.data.ntpServer };

getPaloAltoNtpServers(device) =
foreach match in blockMatches(device.files.config, patternPaloAlto)
let serverList = smatch.data.primaryNtpServer, match.data.secondaryNtpServer]
foreach server in serverList
select { serverId: server };

getCiscoNtpServers(device) =
foreach x in x0]
let lines = (foreach match in blockMatches(device.files.config, patternCisco)
select match.blocks)
foreach line in lines
let lineString = toString(line)
let validRegex = re`^ntp server(?: vrf \S+)? (\d{1,3}(?:\.\d{1,3}){3}|)a-zA-Z0-9.\-]+\.-a-zA-Z]{2,}|{a-zA-Z0-9.\-]+)`
foreach serverMatch in regexMatches(lineString, validRegex)
select { serverId: serverMatch.data."1"] };

getIosXrNtpServers(device) =
foreach match in blockMatches(device.files.config, patternIosXr)
select { serverId: match.data.ntpServer };

getAristaNtpServers(device) =
foreach x in x0]
let lines = (foreach match in blockMatches(device.files.config, patternArista)
select match.blocks)
foreach line in lines
let lineString = toString(line)
let validRegex = re`^ntp server(?: vrf \S+)? (\d{1,3}(?:\.\d{1,3}){3}|)a-zA-Z0-9.\-]+\.-a-zA-Z]{2,}|{a-zA-Z0-9.\-]+)`
foreach serverMatch in regexMatches(lineString, validRegex)
select { serverId: serverMatch.data."1"] };

getF5NtpServers(device) =
foreach command in device.outputs.commands
where command.commandText == "list sys ntp"
let commandString = command.response
let editedCommandString = replace(commandString, "{", "")
let editedCommandString = replace(editedCommandString, "}", "")
let parsedCommand = parseConfigBlocks(OS.F5, editedCommandString)
foreach match in blockMatches(parsedCommand, patternF5)
foreach entry in match.data.ntpServer
select { serverId: entry };

getJunosNtpServers(device) =
foreach match in blockMatches(device.files.config, patternJunos)
select { serverId: match.data.ntpServer };

getDellNtpServers(device) =
foreach x in x0]
let serverList1 = (foreach match
in blockMatches(device.files.config, patternDell)
select match.data.ntpServer)
let serverList2 = (foreach match
in blockMatches(device.files.config, patternDell2)
select match.data.ntpServer)
let serverList = if length(serverList1) > length(serverList2)
then serverList1
else serverList2
foreach server in serverList
select { serverId: server };

getVersaNtpServers(device) =
foreach match in blockMatches(device.files.config, patternVersa)
select { serverId: match.data.ntpServer };

export getNtpServers(device: Device) =
foreach x in x0]
let ntpServers = when device.platform.os is
FORTINET -> getFortinetNtpServers(device);
PAN_OS -> getPaloAltoNtpServers(device);
IOS -> getCiscoNtpServers(device);
NXOS -> getCiscoNtpServers(device);
IOS_XR -> getIosXrNtpServers(device);
IOS_XE -> getCiscoNtpServers(device);
ASA -> getCiscoNtpServers(device);
ARISTA_EOS -> getAristaNtpServers(device);
F5 -> getF5NtpServers(device);
JUNOS -> getJunosNtpServers(device);
DELL_OS6 -> getDellNtpServers(device);
DELL_OS9 -> getDellNtpServers(device);
DELL_OS10 -> getDellNtpServers(device);
DELL_SONIC -> getDellNtpServers(device);
VERSA -> getVersaNtpServers(device);
otherwise -> &{ serverId: null : String }]
foreach server in ntpServers
select server.serverId;

Create a folder called Helper Functions in the root of your Org Repository, and save the above NQE query as Get NTP Servers.

Then create another query that imports and uses the function as follows:

import "Helper Functions/Get NTP Servers";

foreach device in network.devices
where device.platform.vendor != Vendor.FORWARD_CUSTOM
let ntpServers = getNtpServers(device)
select {
violation: !isPresent(max(ntpServers)),
device: device.name,
vendor: device.platform.vendor,
OS: device.platform.os,
"NTP Servers": ntpServers
}

This query will show a violation if the list ntpServers is empty. Also note that Forward Networks synthetic devices are ignored, since they have no NTP configuration.

Notice the reusable simplicity of the line:

 let ntpServers = getNtpServers(device)

The function can be used within any other NQE query to find the NTP Servers for a given device.

You can create Helper Functions for settings like DNS, login banner, SNMP config, and more and add them to the Helper Functions folder.

A similar approach to that above for getting DNS servers can be seen here:

 

Be the first to reply!

Reply