Merge branch 'ntop:dev' into dev

This commit is contained in:
Vasilis Tako 2021-06-24 16:37:51 +02:00 committed by GitHub
commit 4fe3628db9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 249 additions and 230 deletions

View file

@ -475,7 +475,7 @@ function alert_consts.getAlertType(alert_key, alert_entity_id)
alert_key = tonumber(alert_key)
alert_entity_id = tonumber(alert_entity_id)
if alert_entity_id and alerts_by_id[alert_entity_id] then
if alert_entity_id and alerts_by_id[alert_entity_id] and alerts_by_id[alert_entity_id][alert_key] then
return alerts_by_id[alert_entity_id][alert_key]
end

View file

@ -1,67 +0,0 @@
--
-- (C) 2019-21 - ntop.org
--
-- ##############################################
local other_alert_keys = require "other_alert_keys"
local classes = require "classes"
local alert = require "alert"
-- ##############################################
local alert_behavior_anomaly = classes.class(alert)
local i18n_title = i18n("alerts_dashboard.alert_unexpected_behavior_title", {type = ""})
-- ##############################################
alert_behavior_anomaly.meta = {
alert_key = other_alert_keys.alert_behavior_anomaly,
i18n_title = i18n_title,
icon = "fas fa-fw fa-exclamation",
}
-- ##############################################
-- @brief Prepare an alert table used to generate the alert
-- @param value The value got from the measurement
-- @param lower_bound The lower bound of the measurement
-- @param upper_bound The upper bound of the measurement
-- @return A table with the alert built
function alert_behavior_anomaly:init(entity, type_of_behaviour, value, upper_bound, lower_bound, href)
-- Call the parent constructor
self.super:init()
self.alert_type_params = {
entity = entity,
type_of_behaviour = type_of_behaviour,
value = value,
upper_bound = upper_bound,
lower_bound = lower_bound,
href = href,
}
end
-- #######################################################
-- @brief Format an alert into a human-readable string
-- @param ifid The integer interface id of the generated alert
-- @param alert The alert description table, including alert data such as the generating entity, timestamp, granularity, type
-- @param alert_type_params Table `alert_type_params` as built in the `:init` method
-- @return A human-readable string
function alert_behavior_anomaly.format(ifid, alert, alert_type_params)
return(i18n("alerts_dashboard.unexpected_behavior_anomaly_description",
{
entity = alert_type_params.entity or "",
type_of_behaviour = alert_type_params.type_of_behaviour or "",
value = alert_type_params.value or 0,
lower_bound = alert_type_params.lower_bound or 0,
upper_bound = alert_type_params.upper_bound or 0,
href = alert_type_params.href or "",
}))
end
-- #######################################################
return alert_behavior_anomaly

View file

@ -47,6 +47,8 @@ end
-- @return A human-readable string
function alert_suspicious_file_transfer.format(ifid, alert, alert_type_params)
local res = i18n("alerts_dashboard.suspicious_file_transfer")
local url = alert_type_params["protos.http.last_url"]
local info = ""
if alert_type_params and alert_type_params["protos.http.last_url"] then
local type_icon = ''
@ -58,10 +60,15 @@ function alert_suspicious_file_transfer.format(ifid, alert, alert_type_params)
elseif extn == ".png" or extn == ".jpg" then
type_icon = '<i class="fas fa-fw fa-file-image"></i>'
end
res = i18n("alerts_dashboard.suspicious_file_transfer_url",
{url = shortenString(alert_type_params["protos.http.last_url"], 64),
type_icon = type_icon})
if string.len(url) > 64 then
url = shortenString(alert_type_params["protos.http.last_url"], 64)
info = '<i class="fas fa-question-circle" data-bs-toggle="tooltip" data-bs-placement="bottom" title="'..alert_type_params["protos.http.last_url"]..'"></i>'
end
res = i18n("alerts_dashboard.suspicious_file_transfer_url", {
url = url,
type_icon = type_icon,
info = info,
})
end
return res

View file

@ -0,0 +1,91 @@
--
-- (C) 2019-21 - ntop.org
--
-- ##############################################
local other_alert_keys = require "other_alert_keys"
local classes = require "classes"
local alert = require "alert"
local behavior_utils = require("behavior_utils")
-- ##############################################
local alert_behavior_anomaly = classes.class(alert)
local i18n_title = i18n("alerts_dashboard.alert_unexpected_behavior_title", {type = ""})
-- ##############################################
alert_behavior_anomaly.meta = {
alert_key = other_alert_keys.alert_behavior_anomaly,
i18n_title = i18n_title,
icon = "fas fa-fw fa-exclamation",
}
-- ##############################################
-- @brief Prepare an alert table used to generate the alert
-- @param value The value got from the measurement
-- @param lower_bound The lower bound of the measurement
-- @param upper_bound The upper bound of the measurement
-- @return A table with the alert built
function alert_behavior_anomaly:init(entity, type_of_behavior, value, upper_bound, lower_bound, family_key, timeseries_id)
-- Call the parent constructor
self.super:init()
self.alert_type_params = {
entity = entity,
type_of_behavior = type_of_behavior,
value = value,
upper_bound = upper_bound,
lower_bound = lower_bound,
family_key = family_key,
timeseries_id = timeseries_id,
}
end
-- #######################################################
-- @brief Format an alert into a human-readable string
-- @param ifid The integer interface id of the generated alert
-- @param alert The alert description table, including alert data such as the generating entity, timestamp, granularity, type
-- @param alert_type_params Table `alert_type_params` as built in the `:init` method
-- @return A human-readable string
function alert_behavior_anomaly.format(ifid, alert, alert_type_params)
local href = ""
local type_of_behavior = ""
-- Name of the behavior type, e.g. Score
if alert_type_params.type_of_behavior then
type_of_behavior = i18n("alert_behaviors." .. alert_type_params.type_of_behavior)
end
-- Generating the href for the timeserie
if ntop.isEnterpriseL() then
if alert_type_params["family_key"] and alert_type_params["timeseries_id"] then
-- 10 minutes before and 10 minutes after the alert
local alert_time = tonumber(alert.tstamp)
local curr_time = '&epoch_begin=' .. tonumber(alert_time - 600) .. '&epoch_end=' .. tonumber(alert_time + 600)
local timeseries_table = behavior_utils.get_behavior_timeseries_utils(alert_type_params["family_key"])
href = timeseries_table["page_path"] .. "?" .. timeseries_table["timeseries_id"] .. "=" .. alert_type_params["timeseries_id"] ..
"&ifid=" .. interface.getId() .. "&page=historical&ts_schema=" .. timeseries_table["schema_id"] .. "%3A" .. alert_type_params.type_of_behavior ..
"&zoom=30m" .. curr_time
end
end
return(i18n("alerts_dashboard.unexpected_behavior_anomaly_description", {
entity = alert_type_params.entity or "",
type_of_behavior = type_of_behavior,
value = alert_type_params.value or 0,
lower_bound = alert_type_params.lower_bound or 0,
upper_bound = alert_type_params.upper_bound or 0,
href = href,
}))
end
-- #######################################################
return alert_behavior_anomaly

View file

@ -32,14 +32,16 @@ alert_periodic_activity_not_executed.meta = {
-- ##############################################
-- @brief Prepare an alert table used to generate the alert
-- @param ps_name A string with the name of the periodic activity
-- @param last_queued_time The time when the periodic activity was executed for the last time, as a unix epoch
-- @return A table with the alert built
function alert_periodic_activity_not_executed:init(last_queued_time)
function alert_periodic_activity_not_executed:init(ps_name, last_queued_time)
-- Call the parent constructor
self.super:init()
self.alert_type_params = {
last_queued_time = last_queued_time,
ps_name = ps_name,
last_queued_time = last_queued_time,
}
end

View file

@ -34,17 +34,19 @@ end
function interface_alert_store:insert(alert)
local name = getInterfaceName(alert.ifid)
local alias = getHumanReadableInterfaceName(name)
local subtype = alert.subtype or ''
local insert_stmt = string.format("INSERT INTO %s "..
"(alert_id, tstamp, tstamp_end, severity, score, ifid, name, alias, granularity, json) "..
"VALUES (%u, %u, %u, %u, %u, %d, '%s', '%s', %u, '%s'); ",
self._table_name,
"(alert_id, tstamp, tstamp_end, severity, score, ifid, subtype, name, alias, granularity, json) "..
"VALUES (%u, %u, %u, %u, %u, %d, '%s', '%s', '%s', %u, '%s'); ",
self._table_name,
alert.alert_id,
alert.tstamp,
alert.tstamp_end,
ntop.mapScoreToSeverity(alert.score),
alert.score,
alert.ifid,
self:_escape(subtype),
self:_escape(name),
self:_escape(alias),
alert.granularity,
@ -66,6 +68,7 @@ end
local RNAME = {
ALERT_NAME = { name = "alert_name", export = true},
SUBTYPE = { name = "subtype", export = true},
MSG = { name = "msg", export = true, elements = {"name", "value", "description"}}
}
@ -78,10 +81,12 @@ function interface_alert_store:format_record(value, no_html)
local record = self:format_json_record_common(value, alert_entities.interface.entity_id, no_html)
local alert_name = alert_consts.alertTypeLabel(tonumber(value["alert_id"]), no_html, alert_entities.interface.entity_id)
local subtype = value.subtype
local alert_info = alert_utils.getAlertInfo(value)
local msg = alert_utils.formatAlertMessage(interface.getId(), value, alert_info)
record[RNAME.ALERT_NAME.name] = alert_name
record[RNAME.SUBTYPE.name] = subtype
if string.lower(noHtml(msg)) == string.lower(noHtml(alert_name)) then
msg = ""

View file

@ -106,7 +106,6 @@ end
function network_alert_store:format_record(value, no_html)
local record = self:format_json_record_common(value, alert_entities.network.entity_id, no_html)
local alert_id_label = alert_consts.alertTypeLabel(tonumber(value["alert_id"]), no_html)
local alert_name = alert_consts.alertTypeLabel(tonumber(value["alert_id"]), no_html, alert_entities.network.entity_id)
local alert_info = alert_utils.getAlertInfo(value)
local msg = alert_utils.formatAlertMessage(ifid, value, alert_info)

View file

@ -777,6 +777,8 @@ function alert_utils.notify_ntopng_stop()
return(notify_ntopng_status(false))
end
-- #####################################
function alert_utils.formatBehaviorAlert(params, anomalies, stats, id, subtype, name)
-- Cycle throught the behavior stats
for anomaly_type, anomaly_table in pairs(anomalies) do
@ -789,19 +791,20 @@ function alert_utils.formatBehaviorAlert(params, anomalies, stats, id, subtype,
lower_bound = anomaly_table["formatter"](lower_bound)
upper_bound = anomaly_table["formatter"](upper_bound)
end
local alert = alert_consts.alert_types.alert_behavior_anomaly.new(
i18n(subtype .. "_id", {id = name or id}),
i18n("alert_behaviors." .. anomaly_type),
anomaly_type,
value,
lower_bound,
upper_bound,
anomaly_table["href"]
anomaly_table["family_key"],
id
)
alert:set_score_warning()
alert:set_granularity(params.granularity)
alert:set_subtype(subtype .. "_" .. id)
alert:set_subtype(name)
-- Trigger an alert if an anomaly is found
if anomaly_table["anomaly"] == true then

View file

@ -0,0 +1,30 @@
--
-- (C) 2020-21 - ntop.org
--
local behavior_utils = {}
-- ##############################################
local behavior_table = {
asn = {
page_path = "/lua/as_details.lua",
timeseries_id = "asn",
schema_id = "asn",
},
network = {
page_path = "/lua/network_details.lua",
timeseries_id = "network",
schema_id = "subnet",
}
}
-- ##############################################
function behavior_utils.get_behavior_timeseries_utils(family_key)
return behavior_table[family_key]
end
-- ##############################################
return behavior_utils

View file

@ -1087,8 +1087,8 @@ if ntop.isPro() then
{schema="iface:score_anomalies", label=i18n("graphs.iface_score_anomalies")},
{schema="iface:score_behavior", label=i18n("graphs.iface_score_behavior"), split_directions = true --[[ split RX and TX directions ]], metrics_labels = {i18n("graphs.score"), i18n("graphs.lower_bound"), i18n("graphs.upper_bound")}},
{schema="iface:traffic_anomalies", label=i18n("graphs.iface_traffic_anomalies")},
{schema="iface:traffic_rx_behavior", label=i18n("graphs.iface_traffic_rx_behavior"), split_directions = true --[[ split RX and TX directions ]], value_formatter = {"fbits"}, metrics_labels = {i18n("graphs.traffic_rcvd"), i18n("graphs.lower_bound"), i18n("graphs.upper_bound")}},
{schema="iface:traffic_tx_behavior", label=i18n("graphs.iface_traffic_tx_behavior"), split_directions = true --[[ split RX and TX directions ]], value_formatter = {"fbits"}, metrics_labels = {i18n("graphs.traffic_sent"), i18n("graphs.lower_bound"), i18n("graphs.upper_bound")}},
{schema="iface:traffic_rx_behavior", label=i18n("graphs.iface_traffic_rx_behavior"), split_directions = true --[[ split RX and TX directions ]], value_formatter = {"NtopUtils.fbits"}, metrics_labels = {i18n("graphs.traffic_rcvd"), i18n("graphs.lower_bound"), i18n("graphs.upper_bound")}},
{schema="iface:traffic_tx_behavior", label=i18n("graphs.iface_traffic_tx_behavior"), split_directions = true --[[ split RX and TX directions ]], value_formatter = {"NtopUtils.fbits"}, metrics_labels = {i18n("graphs.traffic_sent"), i18n("graphs.lower_bound"), i18n("graphs.upper_bound")}},
}
default_timeseries = table.merge(pro_timeseries, default_timeseries)

View file

@ -584,7 +584,7 @@ function page_utils.print_menubar()
local navbar_style = _POST["toggle_theme"] or ntop.getPref("ntopng.user." .. _SESSION["user"] .. ".theme")
if ((navbar_style == nil) or (navbar_style == "white")) then
if ((navbar_style == nil) or (navbar_style == "white") or (navbar_style == "")) then
navbar_style = "default"
end

View file

@ -265,75 +265,6 @@ local function create_rrd(schema, path, timestamp)
return true
end
-- ##############################################
local function handle_old_rrd_tune(schema, rrdfile, timestamp)
-- In this case the only thing we can do is to remove the file and create a new one
if ntop.getCache("ntopng.cache.rrd_format_change_warning_shown") ~= "1" then
traceError(TRACE_WARNING, TRACE_CONSOLE, "RRD format change detected, incompatible RRDs will be moved to '.rrd.bak' files")
ntop.setCache("ntopng.cache.rrd_format_change_warning_shown", "1")
end
os.rename(rrdfile, rrdfile .. ".bak")
if(not create_rrd(schema, rrdfile, timestamp)) then
return false
end
return true
end
local function add_missing_ds(schema, rrdfile, cur_ds, timestamp)
local cur_metrics = map_metrics_to_rrd_columns(cur_ds)
local new_metrics = map_metrics_to_rrd_columns(#schema._metrics)
if((cur_metrics == nil) or (new_metrics == nil)) then
return false
end
if cur_ds >= #new_metrics then
return false
end
traceError(TRACE_INFO, TRACE_CONSOLE, "RRD format changed [schema=".. schema.name .."], trying to fix " .. rrdfile)
local params = {rrdfile, }
local heartbeat = schema.options.rrd_heartbeat or (schema.options.insertion_step * 2)
local rrd_type = type_to_rrdtype[schema.options.metrics_type]
for idx, metric in ipairs(schema._metrics) do
local old_name = cur_metrics[idx]
local new_name = new_metrics[idx]
if old_name == nil then
params[#params + 1] = "DS:" .. new_name .. ":" .. rrd_type .. ':' .. heartbeat .. ':U:U'
elseif old_name ~= new_name then
params[#params + 1] = "--data-source-rename"
params[#params + 1] = old_name ..":" .. new_name
end
end
local err = ntop.rrd_tune(table.unpack(params))
if(err ~= nil) then
if(string.find(err, "unknown data source name") ~= nil) then
-- the RRD was already mangled by incompatible rrd_tune
return handle_old_rrd_tune(schema, rrdfile, timestamp)
else
traceError(TRACE_ERROR, TRACE_CONSOLE, err)
return false
end
end
-- Double check as some older implementations do not support adding a column and will silently fail
local last_update, num_ds = ntop.rrd_lastupdate(rrdfile)
if num_ds ~= #new_metrics then
return handle_old_rrd_tune(schema, rrdfile, timestamp)
end
traceError(TRACE_INFO, TRACE_CONSOLE, "RRD successfully fixed: " .. rrdfile)
return true
end
-- ###############################################
-- Converts a number (either decimal or integer) to a string
@ -371,7 +302,7 @@ end
-- ##############################################
local function update_rrd(schema, rrdfile, timestamp, data, dont_recover)
local function update_rrd(schema, rrdfile, timestamp, data)
local params = { number_to_rrd_string(timestamp), }
if isDebugEnabled() then
@ -401,39 +332,8 @@ local function update_rrd(schema, rrdfile, timestamp, data, dont_recover)
end
local err = ntop.rrd_update(rrdfile, table.unpack(params))
if(err ~= nil) then
if(dont_recover ~= true) then
-- Try to recover
local last_update, num_ds = ntop.rrd_lastupdate(rrdfile)
local retry = false
if((num_ds ~= nil) and (num_ds < #schema._metrics)) then
if add_missing_ds(schema, rrdfile, num_ds, timestamp) then
retry = true
end
elseif((num_ds == 2) and (schema.name == "iface:traffic")) then
-- The RRD is corrupted due to collision between "iface:traffic" and "evexporter_iface:traffic"
traceError(TRACE_WARNING, TRACE_CONSOLE, "'evexporter_iface:traffic' schema collision detected on " .. rrdfile .. ", moving RRD to .old")
local rv, errmsg = os.rename(rrdfile, rrdfile..".old")
if(rv == nil) then
traceError(TRACE_ERROR, TRACE_CONSOLE, errmsg)
return false
end
if(not create_rrd(schema, rrdfile, timestamp)) then
return false
end
retry = true
end
if retry then
-- Problem possibly fixed, retry
return update_rrd(schema, rrdfile, timestamp, data, true --[[ do not recovery again ]])
end
end
traceError(TRACE_ERROR, TRACE_CONSOLE, err)
return false
end