Fortinet Interface Configuration and Verification Method

  • 13 April 2024
  • 2 replies
  • 91 views

Userlevel 3

How do you quickly verify that only specific Fortinet interfaces are configured for ssh, or ping, or fgfm (FortiGate to FortiManager Protocol)?  Or, how can you quickly report on which interfaces are in which VDOM (Virtual Domain) or VRF (Virtual Routing and Forwarding)? 

(You know what the answer is already… It’s NQE, of course).  

The purpose of this NQE example is to showcase the use of:

  • Customer Commands - In this case, Fortinet “show system interface”.
  • Using the blockMatches() NQE function to match variable blocks.
  • Using an if, else loop to iterate through variable block values.
  • Use the tabular output report to search / filter across all of the Fortinet devices.

Building upon @cariddir examples of using device specific commands and adding Custom Commands, this NQE uses techniques shown in his post.

Additionally, leveraging @Mike’s if Expression tutorial to iterate through the variable Fortinet interface “set” values. 

 

Fortinet interface configuration options are extensive per the Fortinet documentation.
https://docs.fortinet.com/document/fortigate/6.4.3/cli-reference/8620/system-interface

You may need to find where a specific configuration exists or doesn’t exist. 

Therefore you may want to add additional “set” value searches in your report.  To do so, add the “set” value to a new if, else loop and then add a select output.  Don’t forget to add the “,” after the select statement.  (Common mistake).

 

Note that there are two methods to achieve the parsing of the Fortinet interface configuration.  One is a Custom Command.  The alternative is to parse the commandType == CommandType.INTERFACES output.  Both methods are noted in the NQE comments near the code block.

Searching the NQE IDE shows the output of “show system interface” is gathered for FORTINET device types and attributed to Command.Type INTERFACES.

Show me the NQE already.  OK, the logic is fairly straightforward.  Match a fixed and variable patten.  Determine which Fortinet “set” values to output.  And then display those configuration values in a tabular format to able to searching and filter for analysis. 

/**
* @intent For Fortinet devices, parse the Custom Command "show system interface" to output a tabular format of all interfaces and specific interesting configured values.
* @description For Fortinet devices, parse the Custom Command "show system interface" to output a tabular format of all interfaces and specific interesting configured values.
* Use a blockMatches() method to match consistent values and one set for the variable values.
* Iterate through the variable element and value pairs to select / print the interesting interface specific configurations using an if statement.
* *** CONFIGURE a CUSTOM COMMAND "show system interface" for os type Fortinet ***
* Reference: Fortinet config system interface options - https://docs.fortinet.com/document/fortigate/6.4.3/cli-reference/8620/system-interface
*/
pattern_int =
```
config system interface
edit {portname:string}
set vdom {vdom:string}
set type {inttype:string}
set {element:string} {value:(string*)}
```;

/*
* Parse through the following variable values:
* set description "<string>"
* set alias "<string>"
* set mode dhcp
* set ip <ip-address> <subnet-mask>
* set status down
* set role <lan, wan, primary, secondary>
* ... etc
*/

foreach device in network.devices
where device.platform.vendor == Vendor.FORTINET
let outputs = device.outputs
foreach command in outputs.commands
// ** Uncomment out the next two lines to use a Custom Command
// where command.commandType == CommandType.CUSTOM
// where command.commandText == "show system interface"
// ** Comment out the next line if using Custom Command
where command.commandType == CommandType.INTERFACES
let configurations = parseConfigBlocks(OS.FORTINET, command.response)
// This next foreach() will parse out the fixed values specified in the pattern_int section.
foreach int_child in blockMatches(configurations, pattern_int)
// Evaluate the element:value variable configuration block, specifically the element, by using a string and matches() function.
let int_child_element = toString(int_child.data.element)

// Enumerate all the values that are variable
let status =
if matches(int_child_element,"status")
then int_child.data.value
else [""]

let mode =
if matches(int_child_element,"mode")
then int_child.data.value
else [""]

let alias =
if matches(int_child_element,"alias")
then int_child.data.value
else [""]

let allowaccess =
if matches(int_child_element,"allowaccess")
then int_child.data.value
else [""]

let ip =
if matches(int_child_element,"ip")
then int_child.data.value
else [""]

let description =
if matches(int_child_element,"description")
then int_child.data.value
else [""]

let fail_detect_option =
if matches(int_child_element,"fail-detect-option")
then int_child.data.value
else [""]


// Output the constant and variable values
select {
deviceName: device.name,
interface: int_child.data.portname,
vdom: int_child.data.vdom,
description: description,
type: int_child.data.inttype,
fail_detect_option: fail_detect_option,
status: status,
mode: mode,
alias: alias,
allowaccess: allowaccess,
ip: ip,
}

 

The following image is a partial output of the NQE.  The output value links to the configuration section for that device and highlights it.  I prefer this method rather than a string output to achieve dual purposes.  One is to be able to filter the NQE output by various columns, the same as a string value.  And the other is to open a window to the referenced device configuration block for analysis.

 

The only downfall is that since some aliases or descriptions may have spaces, each word is considered an individual value.  A little nit IMHO as the benefit outweighs the perceived issue.  In this example, the alias is “Port 1 native11”. 

 

Clicking on the interface “naf.root” interface link will open a new windows with that area of the device configuration highlighted.

 

Let’s filter for ssh in the allowaccess column to enumerate all of the interfaces with ssh enabled.  Perhaps this is for a security audit.  Or, perhaps you need to audit or verify that ping is enabled on all tunnel interfaces.

Hovering over the output shows all of the values.  The ellipsis (...) indicates that there are additional values. 

Selecting the NQE allowaccess output will pop up another window that details and highlights that specific configuration block for analysis.

 

What else is possible?  How about adding a violation and add this NQE to the NQE Verifications that are run during Snapshot parsing to alert on improper configurations?  With NQE and some simple coding, the information and insights are at your fingertips.

Feel free to give this a try and provide feedback.


2 replies

I think you could remove those 7 blocks of let-if-then-else blocks by using a helper method, which would shorten this query.

pattern_int = ...;

// New helper function
get(data, field, payload) = if matches(data, field) then payload else [""];

foreach device in network.devices
...
let int_child_element = toString(int_child.data.element)

// No more let-if-then-else blocks, just 7 lines
let status = get(int_child_element, "status", int_child.data.value)
let mode = get(int_child_element, "mode", int_child.data.value)
let alias = get(int_child_element, "alias", int_child.data.value)
let allowaccess = get(int_child_element, "allowaccess", int_child.data.value)
let ip = get(int_child_element, "ip", int_child.data.value)
let description = get(int_child_element, "description", int_child.data.value)
let fail_detect_option = get(int_child_element, "fail-detect-option", int_child.data.value)

 

Userlevel 3

@musa - Indeed cleaner coding with the helper function to process the variable interface configuration options.  Thanks!

Reply