N2OS UserManual SDK 23.1.0
N2OS UserManual SDK 23.1.0
Legal notices
Publication Date
June 2023
Copyright
Copyright © 2013-2023, Nozomi Networks. All rights reserved.
Nozomi Networks believes the information it furnishes to be
accurate and reliable. However, Nozomi Networks assumes no
responsibility for the use of this information, nor any infringement of
patents or other rights of third parties which may result from its use.
No license is granted by implication or otherwise under any patent,
copyright, or other intellectual property right of Nozomi Networks
except as specifically described by applicable user licenses. Nozomi
Networks reserves the right to change specifications at any time
without notice.
Table of Contents
Chapter 3: OpenAPI.............................................................................. 35
Setup............................................................................................................................................36
Errors........................................................................................................................................... 38
Query endpoint............................................................................................................................ 39
CLI endpoint................................................................................................................................ 41
Import CSV endpoint...................................................................................................................42
Import JSON endpoint.................................................................................................................43
Alerts endpoint.............................................................................................................................44
Trace endpoint.............................................................................................................................49
Users endpoint............................................................................................................................ 51
PCAPs endpoint.......................................................................................................................... 56
Reports endpoint......................................................................................................................... 61
Report templates endpoint.......................................................................................................... 64
Quarantine endpoint.................................................................................................................... 66
Threat intelligence....................................................................................................................... 67
1
Scriptable protocols
Topics: In this manual we will cover the Lua scripting API for building a
custom protocol decoder.
• Setup
• Writing a scriptable protocol
• API reference
| Scriptable protocols | 8
Setup
To add a new scriptable protocol:
1. Copy the Lua script in /data/scriptable_protocols/
2. Configure Guardian with this rule conf.user configure probe scriptable-protocol
<protocol_name> <script_name> in CLI (<script_name> is the name of the file including the
extension)
3. Execute service n2osids stop, the ids process will be restarted automatically.
After this steps the new protocol is loaded in Guardian and will analyze the network traffic.
| Scriptable protocols | 9
function can_handle()
return true
end
From the example we can see that the only mandatory thing to do is to define a function called
can_handle which returns true if it recognize the target protocol.
Of course this implementation is not very useful and it will try to handle every packet so let's write
something more complex to detect and analyze some modbus traffic:
function can_handle()
return packet.source_port() == 502 or packet.destination_port() == 502
end
Here we can see a usage of the API to retrieve the packet ports. In this way the check is a bit more
accurate but it's still insufficient to detect a modbus packet in the real world.
Let's start to do some deep packet inspection:
function can_handle()
if data_size() < 8 then
return false
end
fwd(2)
local has_right_protocol_id = consume_n_uint16() == 0
local expected_length = consume_n_uint16()
WARNING: don't use global variables. Variables defined outside of the can_handle and
update_status functions are global and their status is shared across every session of the same
protocol.
NOTE: the fwd and consume_* functions will move forward the payload pointer.
NOTE: the result of the remaining_size function depends on the position of the payload pointer.
In this example we use the API to inspect the content of the payload. First we check that there are
enough bytes, a modbus packet is at least 8 bytes long. Then we check the port in the same we did
in the previous example, then we skip two bytes with the function fwd and we read the next two 16 bit
integers. We check that the protocol id is zero and that the length written in the packet match with the
remaining bytes in our payload. If every check pass we return true saying to Guardian that the next
packets in this session should be analyzed by this protocol decoder.
| Scriptable protocols | 10
A protocol with just the can_handle function implemented will only create the node and the session in
the Network but the link is still missing from the graph, no additional information will be displayed in the
Process information.
To extract more information from the modbus packets we are going to implement the update_status
function:
function get_protocol_type()
return ProtocolType.SCADA
end
function can_handle()
return is_modbus()
end
function update_status()
if not is_modbus() then
return
end
if is_request then
is_packet_from_src_to_dst(true)
set_roles("consumer", "producer")
if fc == 6 then
local address = consume_n_uint16()
execute_update_with_variable(FunctionCode.new(fc), RtuId.new(rtu_id),
"r"..tostring(address), value)
return
end
end
execute_update()
end
NOTE: to avoid duplication we created a is_modbus function from the content of the previous
can_handle function.
NOTE: the is_modbus function has the effect to advance the payload pointer by 6 bytes, so when can
directly read the rtu_id without further payload pointer manipulations.
NOTE: we defined the get_protocol_type function to define the protocol type
In this example of update_status we read more data from the payload and we decode the
write single register request. We can understand the direction of the communication so we call
is_packet_from_src_to_dst with true to notify Guardian and create a link and we call
set_roles to set the roles on the involved nodes.
To insert a variable in Guardian there is the execute_update_with_variable function, it takes 4
arguments: the function code, the rtu id, the variable name and the value. The FunctionCode and
RtuId objects can be constructed from a string or a number, the DataValue object can be constructed
with the empty constructor and then filled with the available information.
| Scriptable protocols | 11
With the next example we cover a more complex case and we store some data in the session to handle
a request and a response:
local PENDING_FC = 1
local PENDING_START_ADDR = 2
local PENDING_REG_COUNT = 3
function update_status()
if not is_modbus() then
return
end
rwd()
if is_request then
is_packet_from_src_to_dst(true)
set_roles("consumer", "producer")
session.set_pending_request_number(transaction_id, PENDING_FC, fc)
if fc == 3 then
if remaining_size() < 4 then
return
end
session.set_pending_request_number(transaction_id, PENDING_START_ADDR,
start_addr)
session.set_pending_request_number(transaction_id, PENDING_REG_COUNT,
registers_count)
end
else
is_packet_from_src_to_dst(false)
local req_fc = session.read_pending_request_number(transaction_id,
PENDING_FC)
if fc == req_fc then
if fc == 3 then
local start_addr = session.read_pending_request_number(transaction_id,
PENDING_START_ADDR)
local reg_count = session.read_pending_request_number(transaction_id,
PENDING_REG_COUNT)
session.close_pending_request(transaction_id)
if remaining_size() ~= byte_count or
reg_count * 2 ~= remaining_size() then
send_alert_malformed_packet("Packet is too small")
return
end
for i = 0, reg_count - 1, 1 do
local value = DataValue.new()
value.value = consume_n_uint16()
| Scriptable protocols | 12
value.cause = DataCause.READ_SCAN
value.type = DataType.ANALOG
value.time = packet.time()
execute_update_with_variable(FunctionCode.new(fc),
RtuId.new(rtu_id),
"r"..tostring(start_addr+i),
value)
end
return
end
end
end
execute_update()
end
This time we are focusing on the read holding register function code, to understand the communication
and create a variable we need to analyze both the request and the response and we need to keep
some data from the request and use it in the response. To achieve this we can use the functions
provided by the session object.
| Scriptable protocols | 13
API reference
Data types
Class FunctionCode
Constructors • FunctionCode.new(<string>)
• FunctionCode.new(<number>)
Class RtuId
Constructors • RtuId.new(<string>)
• RtuId.new(<number>)
Class DataValue
Constructors • DataValue.new()
Class Variable
Methods • set_label(<string>)
Class Node
Methods • set_property(<key>, <value>)
• get_property(<key>)
• delete_property(<key>)
• set_label(<label>)
Enum DataCause
Values • DataCause.READ_SCAN
• DataCause.READ_CYCLIC
• DataCause.READ_EVENT
• DataCause.WRITE
Enum DataType
| Scriptable protocols | 14
Values • DataType.ANALOG
• the Analog type represents a floating point number
• DataType.DIGITAL
• the Digital type represents a boolean type and can be either 0 or 1
• DataType.BITSTRING
• the Bitstring type represents a raw value in the form of a sequence of
0 and 1, e.g. "00101110"
• DataType.STRING
• the String type represents a value in the form of a sequence of
printable characters
• DataType.DOUBLEPOINT
• the Double Point type represents a boolean value with an additional
degree of redundancy. It is commonly used in protocols such as
DNP3, IEC 104 or IEC 61850
• DataType.TIMESTAMP
• the Timestamp type represents a point in time in the format of
milliseconds from the epoch
• Note: Only ANALOG, DIGITAL and DOUBLEPOINT types are kept in
consideration by the Process Learning Engine when detecting deviations
from the baseline.
Enum ProtocolType
Values • ProtocolType.SCADA
• ProtocolType.NETWORK
• ProtocolType.IoT
Functions
Syntax data(<index>)
Parameters • index: the position of the byte to read, starting from 0
Description Return the value of the byte from the specified position, return 0 if index is
out of bounds
Syntax data_size()
Description Return the total size of the payload
Syntax remaining_size()
Description Return the size of the payload from the pointer to the end. The result
depends on the usage of functions fwd(), rwd() and consume_*().
Syntax fwd(<amount>)
Parameters • amount: the number of bytes to skip
Syntax rwd()
Description Move the payload pointer to the beginning of the payload.
| Scriptable protocols | 15
Syntax read_uint8()
Description Read an unsigned 8bit integer at the payload pointer position.
Syntax read_int8()
Description Read an signed 8bit integer at the payload pointer position.
Syntax read_n_uint16()
Description Read a network order unsigned 16bit integer at the payload pointer position.
Syntax read_h_uint16()
Description Read a host order unsigned 16bit integer at the payload pointer position.
Syntax read_n_int16()
Description Read a network order signed 16bit integer at the payload pointer position.
Syntax read_h_int16()
Description Read a host order signed 16bit integer at the payload pointer position.
Syntax read_n_uint32()
Description Read a network order unsigned 32bit integer at the payload pointer position.
Syntax read_h_uint32()
Description Read a host order unsigned 32bit integer at the payload pointer position.
Syntax read_n_int32()
Description Read a network order signed 32bit integer at the payload pointer position.
Syntax read_h_int32()
Description Read a host order signed 32bit integer at the payload pointer position.
Syntax read_n_uint64()
Description Read a network order unsigned 64bit integer at the payload pointer position.
Syntax read_h_uint64()
Description Read a host order unsigned 64bit integer at the payload pointer position.
Syntax read_n_int64()
Description Read a network order signed 64bit integer at the payload pointer position.
Syntax read_h_int64()
Description Read a host order signed 64bit integer at the payload pointer position.
Syntax read_n_float()
Description Read a network order float at the payload pointer position.
| Scriptable protocols | 16
Syntax read_h_float()
Description Read a host order float at the payload pointer position.
Syntax read_n_double()
Description Read a network order double at the payload pointer position.
Syntax read_h_double()
Description Read a host order double at the payload pointer position.
Syntax read_string()
Description Read a string at the payload pointer position until the null terminator.
Syntax read_string_with_len(str_len)
Description Read a string at the payload pointer position for str_len bytes.
Syntax consume_uint8()
Description Read an unsigned 8bit integer at the payload pointer position and move the
pointer after the data.
Syntax consume_int8()
Description Read an signed 8bit integer at the payload pointer position and move the
pointer after the data.
Syntax consume_n_uint16()
Description Read a network order unsigned 16bit integer at the payload pointer position
and move the pointer after the data.
Syntax consume_h_uint16()
Description Read a host order unsigned 16bit integer at the payload pointer position and
move the pointer after the data.
Syntax consume_n_int16()
Description Read a network order signed 16bit integer at the payload pointer position
and move the pointer after the data.
Syntax consume_h_int16()
Description Read a host order signed 16bit integer at the payload pointer position and
move the pointer after the data.
Syntax consume_n_uint32()
Description Read a network order unsigned 32bit integer at the payload pointer position
and move the pointer after the data.
Syntax consume_h_uint32()
Description Read a host order unsigned 32bit integer at the payload pointer position and
move the pointer after the data.
| Scriptable protocols | 17
Syntax consume_n_int32()
Description Read a network order signed 32bit integer at the payload pointer position
and move the pointer after the data.
Syntax consume_h_int32()
Description Read a host order signed 32bit integer at the payload pointer position and
move the pointer after the data.
Syntax consume_n_uint64()
Description Read a network order unsigned 64bit integer at the payload pointer position
and move the pointer after the data.
Syntax consume_h_uint64()
Description Read a host order unsigned 64bit integer at the payload pointer position and
move the pointer after the data.
Syntax consume_n_int64()
Description Read a network order signed 64bit integer at the payload pointer position
and move the pointer after the data.
Syntax consume_h_int64()
Description Read a host order signed 64bit integer at the payload pointer position and
move the pointer after the data.
Syntax consume_n_float()
Description Read a network order float at the payload pointer position and move the
pointer after the data.
Syntax consume_h_float()
Description Read a host order float at the payload pointer position and move the pointer
after the data.
Syntax consume_n_double()
Description Read a network order double at the payload pointer position and move the
pointer after the data.
Syntax consume_h_double()
Description Read a host order double at the payload pointer position and move the
pointer after the data.
Syntax consume_string()
Description Read a string at the payload pointer position until the null terminator and
move the pointer after the data.
Syntax consume_string_with_len(str_len)
Description Read a string at the payload pointer position for str_len bytes and move
the pointer after the data.
| Scriptable protocols | 18
Description Compute the CRC16 of the remaining payload according to the input
parameters. The input parameters for CRC functions can be easily found
online. For example, to get a CRC16/DNP the parameters are: 0x3D65,
0x0000, 0xFFFF, true, true
Description Compute the CRC32 of the remaining payload according to the input
parameters. The input parameters for CRC functions can be easily
found online. For example, to get a plain CRC32 the parameters are:
0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true
Description Set the roles of the involved nodes, valid values are: "consumer",
"producer", "historian", "terminal", "web_server", "dns_server", "db_server",
"time_server", "other"
Syntax set_source_type(<node_type>)
| Scriptable protocols | 19
Description Set the type of the source node, valid values are: "switch", "router", "printer",
"group", "OT_device", "broadcast", "computer"
Syntax variables_are_on_client()
Parameters
Description Notify to Guardian that the variables should be added to the client node
syntax is_packet_from_src_to_dst(<is_from_src>)
parameters • is_from_src: true is the direction is from src to dst, false otherwise
description notify Guardian about the direction of the packet, this function must be
called to obtain a link creation
syntax execute_update()
parameters
description notify Guardian about the a packet, at least one variant of execute_update
should be called for every packet
description notify Guardian about the a packet with a function code and a rtu id
description notify Guardian about the a packet with a function code, a rtu id, a variable
name and a variable value
description notify Guardian about the a packet with a function code, a rtu id, a variable
name, a variable value and a function that give the possibility to directly
access the variable
| Scriptable protocols | 20
syntax AlertFactory.new_net_device()
description raise an alert of type VI:NEW-NET-DEV
syntax AlertFactory.firmware_transfer()
description raise an alert of type SIGN:FIRMWARE-TRANSFER
syntax AlertFactory.protocol_error(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.wrong_time(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.sync_asked_again(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.protocol_flow_anomaly(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.variable_flow_anomaly(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.dhcp_request(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.invalid_ip(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.new_arp(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.duplicated_ip(<reason>)
parameters • reason: a message to be displayed in the alert
| Scriptable protocols | 21
syntax AlertFactory.link_reconnection(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.rst_from_producer(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.tcp_syn(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.tcp_flood(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.protocol_flood(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.mac_flood(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.network_scan(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.cleartext_password(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.ddos_attack(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.unsupported_func(<reason>)
| Scriptable protocols | 22
syntax AlertFactory.illegal_parameters(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.weak_password(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.malware_detected()
description raise an alert of type SIGN:MALWARE-DETECTED
syntax AlertFactory.unknown_rtu(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.missing_variable(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.protocol_injection(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.new_variable(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.new_variable_value(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.device_state_change(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.configuration_change(<reason>)
| Scriptable protocols | 23
syntax AlertFactory.malicious_protocol(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.weak_encryption(<reason>)
parameters • reason: a message to be displayed in the alert
syntax AlertFactory.new_node(<nodeId>)
parameters • nodeId: identifier of the node
syntax AlertFactory.new_target_node(<nodeId>)
parameters • nodeId: identifier of the node
syntax AlertFactory.new_node_malicious_ip(<nodeId>,
<threatName>)
parameters • nodeId: identifier of the node
• threatName: the name of the threat
syntax AlertFactory.configuration_mismatch(<nodeId>,
<triggerId>, <reason>)
parameters • nodeId: identifier of the node
• triggerId: identifier of the triggering engine entity
• reason: a message to be displayed in the alert
syntax AlertFactory.multiple_ot_device_reservations(<sNodeId>,
<dNodeId>, <protocolId>, <bpfFilter>, <protocolType>,
<reason>)
parameters • sNodeId: identifier of the source node
• dNodeId: identifier of the destination node
• protocolId: identifier of the protocol
• bpfFilter: BPF filter
• protocolType: type of the protocol according to the ProtocolType type
• reason: a message to be displayed in the alert
syntax AlertFactory.multiple_unsuccessful_logins(<sNodeId>,
<dNodeId>, <protocolId>, <bpfFilter>, <protocolType>,
<reason>)
| Scriptable protocols | 25
syntax AlertFactory.multiple_access_denied(<sNodeId>,
<dNodeId>, <protocolId>, <bpfFilter>, <protocolType>,
<reason>)
parameters • sNodeId: identifier of the source node
• dNodeId: identifier of the destination node
• protocolId: identifier of the protocol
• bpfFilter: BPF filter
• protocolType: type of the protocol according to the ProtocolType type
• reason: a message to be displayed in the alert
syntax AlertFactory.protocol_injection(<reason>)
parameters • reason: a message to be displayed in the alert
syntax send_alert_malformed_packet(<reason>)
parameters • reason: a message to be displayed in the alert
description notify a captured URL to the system. Note that captured URLs need to
be explicitly enabled by specifying the vi captured_urls enabled
configuration setting.
| Scriptable protocols | 26
description notify a link event to the system. Note that link events need to be explicitly
enabled by specifying the vi link_events enabled configuration
setting.
syntax packet.source_id()
description return the source node id
syntax packet.destination_id()
description return the destination node id
syntax packet.source_ip()
description return the source node ip
syntax packet.destination_ip()
description return the destination node ip
syntax packet.source_mac()
description return the source node mac
syntax packet.destination_mac()
description return the destination node mac
syntax packet.source_port()
description return the source node port
syntax packet.destination_port()
description return the destination node port
syntax packet.is_ip()
description return true if the packet is an ip packet
syntax packet.transport_type()
description return the transport layer type, can be "tcp", "udp", "ethernet", "icmp" or
"unknown"
syntax packet.source_node()
description returns the source node
syntax packet.destination_node()
description returns the destination node
| Scriptable protocols | 27
syntax packet.time()
description return the packet time
syntax session.has_pending_request(<request_id>)
parameters • request_id: a number used to uniquely identify the request
description return true if there are values stored with the request_id
description return true if there are values stored with the request_id and key
syntax session.close_pending_request(<request_id>)
parameters • request_id: a number used to uniquely identify the request
description close the pending request and delete the associated data
syntax log_d(<msg>)
parameters • msg: the message to log
description log a debug message
syntax log_i(<msg>)
parameters • msg: the message to log
syntax log_e(<msg>)
parameters • msg: the message to log
2
Scriptable variables correlation
Topics: The scriptable variables correlation feature allows users to
implement custom checks on the way variable values change over
• Setup time. This chapter covers how such scripts can be implemented and
• Writing a variables correlation used.
script
• API reference
| Scriptable variables correlation | 30
Setup
To add a script for custom variables correlation, follow the steps below:
1. Copy the Lua script in /data/scriptable_variables
2. Configure Guardian with this rule conf.user configure vi scriptable-variable script
<script_name> in CLI (<script_name> is the name of the file including the extension)
3. Execute service n2osids stop, the IDS process will be restarted automatically.
It is advised that after the IDS process gets retarted, the corresponding log file (n2os_ids.log) is
checked:
• If the script was loaded successfully, an INFO log line like the example below will have been output:
• If the script loading has failed, one or more ERROR log lines should be present in the log file,
providing details on what the problem was.
After the above steps the new scriptable variables correlation script will be loaded in Guardian and will
be offered all variable updates.
| Scriptable variables correlation | 31
Function on_receive_variable will be invoked on every variable update that takes place within
Guardian. On every invocation, this function will receive 5 arguments which will provide information on
the new variable value and its context:
• node_id: The identity of the node, to which the variable belongs.
• namespace: Identifier of the variable container, also known as RTU ID. When the variable update is
coming from a protocol that does not support it, it will be empty or hold a fixed, hardcoded value.
• name: The name of the variable being updated.
• data_value: The data value is a table value describing the updated variable value. Consult the
API reference for the fields exposed by this value.
• is_from_config: Variable updates may arrive over traffic or due to configuration commands. This
argument will be true in case the variable update has been administered via command.
By adding logic in the on_receive_variable function, it is possible to make the system respond to
variable update events in user specific ways. For example, the script below will raise a Variable Flow
Anomaly alert, if the variable with name ioa-515 raises above a threshold of 200:
The above example shows how a simple variables correlation script may look like. In practice though
such usages are not expected, since Guardian provides other, easier ways for implementing such
checks (i.e. assertions).
The scriptable variables correlation becomes a much more interesting mechanism when scripts
become stateful: by using top level scope variables, it is possible to maintain state that is consulted and
updated across different script invocations. For example, if we want to introduce a check on the first
derivative of variable ioa-515, the script below may serve as a basis:
local values_per_node = {}
work_table.previous = work_table.current
work_table.current = { time=data_value.time, value=data_value.value }
| Scriptable variables correlation | 32
API reference
The capabilities available for scriptable variables correlation is a subset of those available for scriptable
protocols. This section enumerates all available data types or functions; for more details on them
consult the Scriptable Protocols API Reference.
Data types
• DataValue
• DataCause
• DataType
Functions
• All AlertFactory functions
• All log_* functions
Chapter
3
OpenAPI
Topics: This section describes OpenAPI implementation, which consists of
an HTTP endpoint for executing custom queries.
• Setup
Important: Queries and exports permission is needed to query
• Errors
all OpenAPI endpoints. In addition, there are further Role-Based
• Query endpoint Access Control (RBAC) permissions that restrict API queries to
• CLI endpoint specific tables.
• Import CSV endpoint
OpenAPI methods that change sensor data produce audit logs. For
• Import JSON endpoint example, this happens when a new user is added through OpenAPI
• Alerts endpoint or when an alert is acknowledged. By default, read-only operations
• Trace endpoint do not produce audit logs. It is possible to change this behavior and
• Users endpoint have GET OpenAPI methods produce audit logs by specifying the
• PCAPs endpoint following CLI command:
• Reports endpoint conf.user configure open_api audit get enabled
• Report templates endpoint true
• Quarantine endpoint
• Threat intelligence
| OpenAPI | 36
Setup
To perform a call to the endpoint you need to pass authentication credentials as headers, the examples
provided use Postman, an HTTP client.
Remember to use your Nozomi Networks Solution's web interface IP instead of the example one.
Basic authentication
Nozomi Networks suggests to create dedicated users for OpenAPI usage, with minimal permissions
necessary to access the required data sources.
Token authentication
As an alternative to basic authentication, use OpenAPI keys created from the Web UI to sign in. See
Chapters 3 and 5 in the N2OS User Manual for instructions on creating an OpenAPI key.
Note: Only local users can have OpenAPI keys.
Using token authentication is a two step process. First, use the /api/open/sign_in endpoint with a
valid key to obtain a JWT token.
Then, use the JWT token as bearer token for any successive call to the API.
| OpenAPI | 37
Remarks
1. The JWT token expires 30 minutes after being created. To use the API for a longer time, request a
new token by calling sign_in again.
2. Any number of JWT tokens can be created.
| OpenAPI | 38
Errors
With basic authentication, if you fail to provide valid authentication credentials the expected error will be
401 Unauthorized, as shown below.
With token authentication, when an invalid or expired token is used, the expected error will be 401
Unauthorized. The body of the response will include a description of the problem, as shown below
for the case of an expired token:
If you ask for a data source that does not exist you will receive a proper message in the error field.
Query endpoint
You can manipulate data sources through the use of queries, which are commands piped one after
another. Refer to the Queries chapter of the N2OS User Manual, or go to /#/query in your Nozomi
Networks solution Web UI for examples.
Requirements and restrictions
• Users must have permission to execute API calls.
• Results display the list of queried items.
• We recommend that you use pagination, adding page and count params.
• The page param is the page number to return, and count is the page dimension.
• If count is not provided, the default value is 100,000; if page is not provided, the default page
number is 1.
• If the provided count value is higher than 100,000, no more than 100,000 items are returned.
Example: To see how many nodes are in the system, call the following URL: https://10.0.1.10/
api/open/query/do?query=nodes | count
CLI endpoint
You can apply changes to the system by issuing CLI commands over this endpoint.
The endpoint is located at /api/open/cli and requires to be invoked with the cmd parameter with a
POST.
CLI commands allow to change virtually anything inside the system, please refer to the Configuration
section of the User Manual for a more complete reference.
| OpenAPI | 42
label
firmware_version
vendor
product_name
serial_number
os
mac_address
label
firmware_version
vendor
product_name
serial_number
os
mac_address
Alerts endpoint
A POST to /api/open/alerts/close request allows you to close a group of alerts passed as a
json list of ids in the body of the request. You must also pass as parameter the close_action field
containing delete_rules or learn_rules in case you want to close alerts as security or as change.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role.
2. The input data must be a JSON dictionary containing a ids key whose value must be an array of
alert 'id' and a 'close_action' constant field.
3. In case the request body does not adhere to the format described above the call returns a 422
error.
4. In case the request is well formed, the result will contain the id of the job in charge of the task. You
can monitor the status of the job via the alerts/close/status/:id API.
{
"ids": ["uuid"],
"close_action": "learn_rules"
}
{
"data": [
{
"id": "uuid",
"ack": true
}
]
}
2. As last parameter of the path you need to specify the id of the job returned by the alerts/ack
API.
3. The result will contain the status of the job, which can have one of the following values: SUCCESS,
PENDING or FAIL
4. In case of FAIL status, the error field will report the error reason.
A GET to /api/open/alerts/all request allows you to get the IDs of alerts matching a condition.
You can specify a filter query in the query parameter and an additional parameter named has_trace
to get the status of the corresponding trace.
Requirements and Restrictions
1. The authenticated user has to belong to a group having admin role or with Alerts section enabled.
2. The query parameter should be in Nozomi Networks Query Language format, where the table
name is implicit, i.e. alerts.
3. The has_trace parameter type is boolean.
4. If no alert matches the specified conditions, a 404 error will be returned.
A GET to /api/open/alerts/:id/trace request allows you to get a file containing the trace of the
alert, whose id is specified as a parameter.
| OpenAPI | 47
Trace endpoint
Filter traces
A GET to /api/open/traces/all request allows you to get traces matching a condition. You can
specify a filter query in the query parameter, which is a standard N2OS query condition, applied to the
trace_requests data source. You have to specify the operation parameter defining the requested
operation. So far the only allowed value for the operation parameter is download.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role permission.
2. As a result you will get a file containing the trace or the traces filtered according to the specified
condition.
3. If the trace is still in progress or it is not found, a 422 error with a proper reason string will be
returned.
BPF filter
A GET to /api/open/traces/bpf-filter request allows you to select traces using a BPF filter.
This call returns a job_id, while the actual disk search is performed asynchronously. The search will
return a list of the first PCAP traces that match the filter. The maximum number of PCAP traces is 50
by default and can be configured with the open_api bpf_filter traces_limit setting. There
can’t be more than a limited number of concurrent BPF trace searches at a time. This number is 2
by default and can be configured with the open_api bpf_filter max_concurrent_searches
setting.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role permission.
| OpenAPI | 50
Users endpoint
A GET to /api/open/users allows you to get a list of all the users.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role.
2. The result contains the list of all users.
3. It's possible to use pagination adding page and count params
4. The page param is the number of the page to return, the count is the dimension of the page.
5. If count is nil or 0 the default value will be 100, if page is nil or 0 the request will not be paginated.
6. This api is disabled by default; to enable it add conf.user configure api users enabled
true in CLI.
A GET to /api/open/user_groups allows you to get a list of all the user groups.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role.
2. The result contains the list of all user groups.
3. It's possible to use pagination adding page and count params
4. The page param is the number of the page to return, the count is the dimension of the page.
5. If count is nil or 0 the default value will be 100, if page is nil or 0 the request will not be paginated.
| OpenAPI | 52
A GET to /api/open/users/:id allows you to get the user having the id passed as path parameter.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role.
2. As last parameter of the path you need to specify the id of the user.
3. The result will contain the user
4. In case the user with that id is not found you'll get a 404.
A DELETE to /api/open/users/:id allows you to delete the user having the id passed as path
parameter.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role.
2. As last parameter of the path you need to specify the id of the user.
3. The result will contain the status code 204 for success else the error code
4. In case the user with that id is not found you'll get a 404.
| OpenAPI | 53
{
"username": "user_under_test22",
"password": "aValidP4ss!",
"user_group_ids": [2],
"strategy": "local",
"is_suspended": false,
"should_update_pwd": false,
"ssh_keys": "an_ssh_key",
"allow_root_ssh": true
}
| OpenAPI | 54
A PUT to /api/open/users/:id allows you to update the user with the id passed as path param.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role.
2. As last parameter of the path you need to specify the id of the user you want to update.
3. The input must be a JSON dictionary containing the user field properly populated
4. If the update goes well the call return 204 (No content) response
5. You can't update the password here because updating password is not idempotent so you can't do
via PUT.
6. The fields you can update are listed below.
7. user_group_ids must contain at least one valid id.
{
"username": "user_under_test22",
"strategy": "local",
"user_group_ids": [1,2],
"is_suspended": false,
"should_update_pwd": false,
"ssh_keys": "a_new_key",
"allow_root_ssh": true
}
{
"password": "4ValidP4ssw0rd!"
}
PCAPs endpoint
A GET request to /api/open/pcaps allows you to get the list of all traces available on the machine.
This endpoint lets you to interact with PCAPs that have been uploaded to Guardian from the Upload
traces page of the System section.
A POST request to /api/open/pcaps/upload allows you to upload a trace passed as a file in the
body of the request.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role or with Upload traces section
enabled.
2. The trace should be passed in the form-data section of the request body.
3. In case the request body does not adhere to the format described above, the call returns a 422
error.
4. If the file sent in the request is not a valid trace, the call returns a 422 error along with an error
reason describing the cause of the validation failure.
5. If the request is accepted, the trace will be uploaded.
| OpenAPI | 58
A POST request to /api/open/pcaps/import allows you to import a trace file that is already
present in the machine.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role or with Upload traces section
enabled.
2. The trace file should be present in the /data/tmp directory of the machine.
3. The filename parameter of the request should contain the name of the trace file.
4. In case the request body does not adhere to the format described above, the call returns a 422
error.
5. If the trace file is not a valid trace, the call returns a 422 error along with an error reason describing
the cause of the validation failure.
6. If the request is accepted, the trace will be uploaded.
A PATCH request to /api/open/pcaps allows you to replay a trace that has been previously
uploaded or imported.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role or with Upload traces section
enabled.
2. The trace should be present in the list of the available traces returned by the GET request to /api/
open/pcaps.
3. The id parameter of the request should contain the ID of the trace.
4. The use_packet_time boolean parameter should be set to true if you want to use the time of
the packets; false otherwise.
5. The data_to_reset_before_play parameter should be set to {} if you do not want to reset
data before playing the trace. Otherwise, you need to specify a JSON dictionary with the sections
you want to reset, for example {"alerts": true, "vi": true}. The list of all available
sections is the following:
• alerts_data
• assertions
• learning
• network_data
• process_data
• queries
• smart_polling_data
• timemachine_data
• traces_data
• vi_data
• vulnerability_data
The list above reflects the options available for Data reset in the UI.
6. In case the request body does not adhere to the format described above, the call returns a 422
error.
7. If you specify an ID of a trace that does not exist, the call returns a 404 error.
8. If the request is accepted, the trace will be replayed.
A PATCH request to /api/open/pcaps/note allows you to change the note field of a trace.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role or with Upload traces section
enabled.
| OpenAPI | 60
2. The trace should be present in the list of the available traces returned by the GET request to /api/
open/pcaps.
3. The id parameter of the request should contain the ID of the trace.
4. The note parameter of the request should contain the text you want to change.
5. In case the request body does not adhere to the format described above, the call returns a 422
error.
6. If the request is accepted, the note will be changed.
Reports endpoint
A GET to /api/open/reports allows you to get a list of all the reports generated.
Requirements and Restrictions
1. A user having the permission to execute api.
2. The result contains the list of all the reports.
3. It's possible to use pagination adding page and count params
4. The page param is the number of the page to return, the count is the dimension of the page.
5. If count is nil or 0 the default value will be 100, if page is nil or 0 the request will not be paginated.
6. You can filter the result passing a template_name as query param having value the report
templates name you want filtering on.
A GET to /api/open/reports/:id allows you to get the report having the id passed as path
parameter.
Requirements and Restrictions
1. A user having the permission to execute api.
2. As last parameter of the path you need to specify the id of the report.
3. The result will contain the report.
4. In case the report with that id is not found you'll get a 404.
| OpenAPI | 62
A GET to /api/open/reports/:id/files allows you to download the report having the id passed
as path parameter.
Requirements and Restrictions
1. A user having the permission to execute api.
2. As middle parameter of the path you need to specify the id of the report.
3. The report download will be triggered.
4. In case the report with that id is not found you'll get a 404.
3. In case the request is well formed return a 202 response with the id of the job is taking care of teh
request.
A GET to api/open/reports/jobs/1/status allows you to get the create report job result.
Requirements and Restrictions
1. A user having the permission to execute api.
2. As parameter of the path you need to specify the id of the job.
3. The result will contain the job status
A GET to /api/open/report_templates/:id allows you to get the report template having the id
passed as path parameter.
Requirements and Restrictions
1. A user having the permission to execute api.
2. As last parameter of the path you need to specify the id of the report template.
3. The result will contain the report template.
4. In case the report with that id is not found you'll get a 404.
| OpenAPI | 65
Quarantine endpoint
A GET request to /api/open/quarantine allows you to get a file from the quarantine directory.
Requirements and Restrictions
1. The authenticated user must be in a group having admin role.
2. The full path of the file must be specified in the file parameter and the format should be /data/
quarantine/<NAME>.
3. If you specify a path that does not exist, the call returns a 404 error.
4. If the request is accepted, the result will contain the actual file that Guardian extracted from traffic
and that the Sandbox classified as malicious.
Hint: as shown in the top part of the previous screenshot, the file parameter to be used with the
request can be found in the properties field of SIGN:MALWARE-DETECTED alerts.
| OpenAPI | 67
Threat intelligence
A POST request to /api/open/threat_intelligence allows you to create indicators.
Requirements and restrictions
1. An authenticated user must be in an admin role group or belong to a Threat Intelligence group
with the Allow configuration option switched to ON in the group settings.
2. Json content is represented as a array of contents that allows you to insert more than one (1)
indicator at a time.
3. Type of content must be specified in the type parameter and the value must be: packet_rules,
yara_rules or stix_indicators.
4. Content name must be specified in the name parameter.
5. The content must be specified in the content parameter.
6. If the request is accepted, the result contains the result with an ID as value.
7. The request is rejected if the sensor is connected to a CMC.
4
Data Model reference
Topics: This chapter describes our Data Model reference for query entities.
• alerts
• appliances
• assertions
• assets
• captured_logs
• captured_urls
• function_codes
• health_log
• link_events
• links
• node_cpe_changes
• node_cpes
• node_cves
• node_points
• nodes
• report_files
• sessions_history
• sessions
• variable_history
• variables
| Data Model reference | 70
alerts
Alerts represent events raised by Guardian.
appliances
This query source contains information about the sensors connected to the current CMC or Guardian.
assertions
An assertion represents an automatic check against other query sources.
assets
Assets represent a local, physical system, and can be composed of one or more nodes.
captured_logs
Logs captured passively over the network.
captured_urls
URLs and other protocol calls found in the network. Access to files, requests to DNS, requested URLs
and others are available in this query source.
function_codes
Function codes used in the environment.
health_log
Health-related events about the system - like high resource utilization or hardware-related issues or
events.
| Data Model reference | 76
link_events
Events that can occur on a link, including being available or not.
links
Links are protocol relations between two nodes with a specific protocol. They model the interaction
between nodes.
node_cpe_changes
When a CPE updates, it creates an entry in this query source to track software updates or to detect
software.
node_cpes
Lists Common Platform Enumerations (CPEs), that is software or components connected to a specific
node in the system.
node_cves
Vulnerabilities are matched against current CVEs.
node_points
Data points are polled via Smart Polling from monitored nodes.
nodes
List of nodes, where a node is an L2 or L3 or other entity able to speak some protocol.
report_files
Generated reports available for consultation.
sessions_history
Archived sessions. See the sessions query source for more information.
sessions
Live, mostly open, sessions between nodes. A session is a specific application-level connection
between nodes. A link can hold one or more sessions at a given time.
variable_history
History of values for variables, where history has been enabled.
variables
Variables extracted via DPI from the monitored system.
5
Data integration best practices
Topics: This chapter details best practices when using Nozomi Networks
data integration using Syslog integration or OpenAPI calls.
• OpenAPI data
• Certify Your Integration with
Nozomi
| Data integration best practices | 92
OpenAPI data
API users
Nozomi Networks recommends the practice of creating a user specifically for the purpose of OpenAPI
access. This provides a straightforward demarcation of responsibilities for auditing and tracing.
Best Practice: Create a user specifically to access OpenAPI.
Authentication
Each call to an OpenAPI method requires authentication. OpenAPI currently supports basic
authentication. For example, when using CURL, if you have a username and password for your
OpenAPI user, you would use the following header along with your query:
{
"header": [
All of the headers…
],
"result": [
{ First Node data },
{ Second Node data }
],
"total": 2
}
Note that the original query text “nodes | count” has been URI encoded to nodes%20%7C
%20count.
Note that the language and method of implementation dictate how the URI encoding is accomplished.
ip,label,firmware_version,vendor,product_name,serial_number,mac_address
192.168.1.60,CSV Uploaded Asset 1,1.2.2,ACME,ACME Product
1,abcdefge,00:01:02:03:04:06
192.168.1.61,CSV Uploaded Asset 2,1.2.2,ACME,ACME Product
2,abcdefge,00:11:12:13:14:16
The following command will upload these assets into the Guardian or CMC:
{
"nodes": [
{
"ip": "1.2.3.8",
"label": "JSON_Uploaded_Asset_1",
"mac_address": "00:00:00:11:11:11",
"firmware_version": "1.2.3",
"product_name": "ACME_PLC_2",
"serial_number": "1-789A10-2",
"vendor": "ACME"
},
{
"ip": "1.2.3.3",
"label": "JSON_Uploaded_Asset_2",
"mac_address": "00:00:00:11:11:15",
"firmware_version": "1.2.2",
"product_name": "ACME_PLC_1",
"serial_number": "1-789A10-6",
"vendor": "ACME"
}
]
}
Depending on your CURL implementation, the file may have to be submitted using -d as in the
example below.
The following command uploads these assets into the Guardian or CMC:
| Data integration best practices | 94
Import Commands
Downloading traces
Traces associated with an alert can be downloaded via the API as well. You need the alert ID in
order to accomplish this. The following command downloads a trace associated with an alert ID
<YourAlertID> to the file specified by <YourTraceFile>:
• Once Nozomi technical personnel have approved the materials then the integration will be
considered completed. The integration will then be granted Certified Technology Partner status.