1
0
mirror of https://github.com/nttgin/BGPalerter.git synced 2024-05-19 06:50:08 +00:00

refactoring monitoring to multimatch

This commit is contained in:
Massimo Candela
2023-10-20 00:26:24 +02:00
parent 96958e9b54
commit a795f0ce1b
17 changed files with 168 additions and 136 deletions

View File

@@ -46,7 +46,7 @@ monitors:
channel: misconfiguration
name: asn-monitor
params:
skipPrefixMatchOnDifferentGroups: false
skipPrefixMatch: false
thresholdMinPeers: 3
- file: monitorRPKI

View File

@@ -89,9 +89,9 @@ export default class myMonitor extends Monitor { // It MUST extend Monitor
* This is where the real analysis happens and when the alerts are generated. Place here your complex filtering/analysis.
* The 'filter' function described before is needed to avoid useless calls to the 'monitor' function, which is much more expensive in terms of memory. */
const matchedRule = this.getMoreSpecificMatch(message.prefix); //The method getMoreSpecificMatch is inherited from the super class, it provides the rule in prefixes.yml that matches the current BGP message.
const matchedRules = this.getMoreSpecificMatches(message.prefix); //The method getMoreSpecificMatches is inherited from the super class, it provides the rule in prefixes.yml that matches the current BGP message.
if (matchedRule) { // We matched something in prefixes.yml
for (let matchedRule of matchedRules) { // We matched something in prefixes.yml
const signature = message.originAS.getId() + "-" + message.prefix; // All messages with the same origin AS and prefix will be bundled together. Read above the squash method to understand why.
this.publishAlert(signature, // The method publishAlert is inherited from the super class.

View File

@@ -68,7 +68,7 @@ export default class Config {
channel: "misconfiguration",
name: "asn-monitor",
params: {
skipPrefixMatchOnDifferentGroups: false,
skipPrefixMatch: false,
thresholdMinPeers: 3
}
},

View File

@@ -62,9 +62,7 @@ export default class Input {
// This is to load the prefixes after the application is booted
setTimeout(() => {
this.loadPrefixes()
.then(() => {
this._change();
})
.then(() => this._change())
.catch(error => {
this.logger.log({
level: 'error',
@@ -105,7 +103,12 @@ export default class Input {
};
_change = () => {
for (let item of this.asns) {
item.group = [item.group].flat();
}
for (let item of this.prefixes) {
item.group = [item.group].flat();
this.index.addPrefix(item.prefix, item);
}
@@ -150,15 +153,20 @@ export default class Input {
throw new Error('The method getMonitoredPrefixes MUST be implemented');
};
getMoreSpecificMatch = (prefix, includeIgnoredMorespecifics=false) => {
const matches = this.index.getMatch(prefix, false)
.filter(i => {
return includeIgnoredMorespecifics || !i.ignoreMorespecifics || ipUtils.isEqualPrefix(i.prefix, prefix); // last piece says "or it is not a more specific"
});
return matches.length ? matches[0] : null
};
_filterIgnoreMorespecifics = (prefix, includeIgnoredMorespecifics) => {
return i => {
return includeIgnoredMorespecifics
|| !i.ignoreMorespecifics
|| ipUtils._isEqualPrefix(i.prefix, prefix); // last piece says "or it is not a more specific"
}
}
getMoreSpecificMatches = (prefix, includeIgnoredMorespecifics=false) => {
return this.index.getMatch(prefix, false)
.filter(this._filterIgnoreMorespecifics(prefix, includeIgnoredMorespecifics));
}
getMonitoredASns = () => {
throw new Error('The method getMonitoredASns MUST be implemented');

View File

@@ -74,9 +74,7 @@ export default class InputYml extends Input {
this.prefixes = [];
this.asns = [];
this._loadPrefixes()
.then(() => {
return this._change();
})
.then(() => this._change())
.catch(error => {
this.logger.log({
level: 'error',
@@ -132,7 +130,7 @@ export default class InputYml extends Input {
uniqueAsns[asn] = true;
const item = Object.assign({
asn: new AS(asn),
group: 'default'
group: ['default']
}, monitoredPrefixesFile.options.monitorASns[asn]);
if (item.upstreams && item.upstreams.length) {
@@ -162,7 +160,7 @@ export default class InputYml extends Input {
return Object.assign({
prefix: i,
group: 'default',
group: ['default'],
ignore: false,
excludeMonitors: [],
includeMonitors: [],
@@ -351,7 +349,7 @@ export default class InputYml extends Input {
for (let asnRule of this.asns) {
monitorASns[asnRule.asn.getValue()] = {
group: asnRule.group,
group: [asnRule.group].flat(),
upstreams: asnRule.upstreams ? asnRule.upstreams.numbers : null,
downstreams: asnRule.downstreams ? asnRule.downstreams.numbers : null,
};

View File

@@ -257,19 +257,12 @@ export default class Monitor {
return null;
};
_included = (matched) => {
if (matched.includeMonitors.length > 0) {
return matched.includeMonitors.includes(this.name);
} else {
return !matched.excludeMonitors.includes(this.name);
}
};
getMoreSpecificMatch = (prefix, includeIgnoredMorespecifics, verbose=false) => {
const matched = this.input.getMoreSpecificMatch(prefix, includeIgnoredMorespecifics);
_filterMatched = (verbose) => {
return matched => {
if (matched) {
const included = this._included(matched);
const included = matched.includeMonitors.length > 0
? matched.includeMonitors.includes(this.name)
: !matched.excludeMonitors.includes(this.name);
if (verbose) {
return {
@@ -282,6 +275,13 @@ export default class Monitor {
}
return null;
}
}
getMoreSpecificMatches = (prefix, includeIgnoredMorespecifics, verbose) => {
return this.input.getMoreSpecificMatches(prefix, includeIgnoredMorespecifics)
.map(this._filterMatched(verbose))
.filter(i => !!i);
};
}

View File

@@ -37,7 +37,7 @@ export default class MonitorAS extends Monitor {
constructor(name, channel, params, env, input){
super(name, channel, params, env, input);
this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 3;
this.skipPrefixMatchOnDifferentGroups = !!params?.skipPrefixMatchOnDifferentGroups;
this.skipPrefixMatch = !!params?.skipPrefixMatch;
this.updateMonitoredResources();
};
@@ -84,8 +84,25 @@ export default class MonitorAS extends Monitor {
if (matchedRule) {
const matchedPrefixRule = this.getMoreSpecificMatch(messagePrefix, true);
if (!matchedPrefixRule) {
const matchedPrefixRules = this.getMoreSpecificMatches(messagePrefix, true, false);
if (this.skipPrefixMatch) {
this.publishAlert(messageOrigin.getId().toString() + "-" + messagePrefix,
messageOrigin.getId(),
matchedRule,
message,
{});
for (let matchedPrefixRule of matchedPrefixRules) {
this.publishAlert(messageOrigin.getId().toString() + "-" + messagePrefix,
messageOrigin.getId(),
matchedPrefixRule,
message,
{});
}
} else if (!matchedPrefixRules.length) {
this.publishAlert(messageOrigin.getId().toString() + "-" + messagePrefix,
messageOrigin.getId(),
matchedRule,

View File

@@ -80,15 +80,17 @@ export default class MonitorHijack extends Monitor {
}
}
monitor = (message) =>
new Promise((resolve, reject) => {
monitor = (message) =>{
const messagePrefix = message.prefix;
const matchedRule = this.getMoreSpecificMatch(messagePrefix, false);
const matchedRules = this.getMoreSpecificMatches(messagePrefix, false);
if (matchedRule && !matchedRule.ignore && !matchedRule.asn.includes(message.originAS)) {
for (let matchedRule of matchedRules) {
if (!matchedRule.ignore && !matchedRule.asn.includes(message.originAS)) {
this.validate(message, matchedRule);
resolve(true);
}
});
}
return Promise.resolve(true);
}
}

View File

@@ -62,13 +62,13 @@ export default class MonitorNewPrefix extends Monitor {
return false;
};
monitor = (message) =>
new Promise((resolve, reject) => {
monitor = (message) => {
const messagePrefix = message.prefix;
const matchedRule = this.getMoreSpecificMatch(messagePrefix, false);
const matchedRules = this.getMoreSpecificMatches(messagePrefix, false);
if (matchedRule && !matchedRule.ignore &&
for (let matchedRule of matchedRules) {
if (!matchedRule.ignore &&
matchedRule.asn.includes(message.originAS) &&
!ipUtils._isEqualPrefix(matchedRule.prefix, messagePrefix)) {
@@ -78,8 +78,9 @@ export default class MonitorNewPrefix extends Monitor {
message,
{});
}
}
resolve(true);
});
return Promise.resolve(true);
}
}

View File

@@ -104,20 +104,20 @@ export default class MonitorPath extends Monitor {
}
};
monitor = (message) =>
new Promise((resolve, reject) => {
monitor = (message) => {
const messagePrefix = message.prefix;
const matchedRule = this.getMoreSpecificMatch(messagePrefix, false);
const matchedRules = this.getMoreSpecificMatches(messagePrefix, false);
if (matchedRule && !matchedRule.ignore && matchedRule.path) {
for (let matchedRule of matchedRules) {
if (!matchedRule.ignore && matchedRule.path) {
const pathRules = (matchedRule.path.length) ? matchedRule.path : [ matchedRule.path ];
pathRules.map((pathRule, position) => this.pathRuleCheck(pathRule, position, message, matchedRule));
}
}
resolve(true);
});
return Promise.resolve(true);
};
}

View File

@@ -210,8 +210,11 @@ export default class MonitorROAS extends Monitor {
return Promise.all([...new Set(vrps.map(i => i.prefix))]
.map(prefix => {
const roas = vrps.filter(i => ipUtils.isEqualPrefix(i.prefix, prefix)); // Get only the ROAs for this prefix
const matchedRule = this.getMoreSpecificMatch(prefix, false); // Get the matching rule
if (matchedRule) {
const matchedRules = this.getMoreSpecificMatches(prefix, false); // Get the matching rule
return Promise
.all(matchedRules
.map(matchedRule => {
return this._getExpiringItems(roas)
.then(extra => {
const alertsStrings = [...new Set(roas.map(this._roaToString))];
@@ -230,10 +233,8 @@ export default class MonitorROAS extends Monitor {
matchedRule,
message,
{...extra, vrps, roaExpirationHours: roaExpirationAlertHours, rpkiMetadata: metadata, subType: "roa-expire"});
});
} else {
return Promise.resolve();
}
})
}))
}))
.then(() => alerts);
};
@@ -314,8 +315,9 @@ export default class MonitorROAS extends Monitor {
for (let prefix of [...new Set(roaDiff.map(i => i.prefix))]) {
const roas = roaDiff.filter(i => ipUtils.isEqualPrefix(i.prefix, prefix)); // Get only the ROAs for this prefix
const matchedRule = this.getMoreSpecificMatch(prefix, false); // Get the matching rule
if (matchedRule) {
const matchedRules = this.getMoreSpecificMatches(prefix, false); // Get the matching rule
for (let matchedRule of matchedRules) {
const alertsStrings = [...new Set(roas.map(this._roaToString))];
const message = alertsStrings.length <= 10 ?
`ROAs change detected: ${alertsStrings.join("; ")}` :

View File

@@ -183,18 +183,23 @@ export default class MonitorRPKI extends Monitor {
const messageOrigin = message.originAS;
const prefix = message.prefix;
const matchedPrefixRule = this.getMoreSpecificMatch(prefix, false, true);
const matchedPrefixRules = this.getMoreSpecificMatches(prefix, false, true);
if (matchedPrefixRule && matchedPrefixRule.matched) { // There is a prefix match
if (matchedPrefixRules.length) {
for (let matchedPrefixRule of matchedPrefixRules) {
if (matchedPrefixRule.matched) { // There is a prefix match
if (!matchedPrefixRule.matched.ignore && matchedPrefixRule.included) { // The prefix match is not excluded in any way
this.validate(message, matchedPrefixRule.matched);
}
} else { // No prefix match
}
}
} else {
const matchedASRule = this.getMonitoredAsMatch(messageOrigin); // Try AS match
if (matchedASRule) {
this.validate(message, matchedASRule);
}
}
} catch (error) {
this.logger.log({
level: 'error',

View File

@@ -68,13 +68,12 @@ export default class MonitorVisibility extends Monitor {
}
};
monitor = (message) =>
new Promise((resolve, reject) => {
monitor = (message) => {
const messagePrefix = message.prefix;
const matchedRule = this.getMoreSpecificMatch(messagePrefix, false);
const matchedRules = this.getMoreSpecificMatches(messagePrefix, false);
if (matchedRule && !matchedRule.ignore && ipUtils._isEqualPrefix(matchedRule.prefix, messagePrefix)) {
for (let matchedRule of matchedRules) {
if (!matchedRule.ignore && ipUtils._isEqualPrefix(matchedRule.prefix, messagePrefix)) {
let key = matchedRule.prefix;
@@ -84,8 +83,8 @@ export default class MonitorVisibility extends Monitor {
message,
{});
}
resolve(true);
});
}
return Promise.resolve(true);
}
}

View File

@@ -241,7 +241,7 @@ describe("Core functions", function() {
"description": "rpki valid not monitored AS",
"ignoreMorespecifics": false,
"prefix": "193.0.0.0/21",
"group": "default",
"group": ["default"],
"excludeMonitors" : [],
"includeMonitors": []
},
@@ -250,7 +250,7 @@ describe("Core functions", function() {
"description": "description 1",
"ignoreMorespecifics": false,
"prefix": "165.254.225.0/24",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors" : [],
"includeMonitors": []
@@ -260,7 +260,7 @@ describe("Core functions", function() {
"description": "description 2",
"ignoreMorespecifics": false,
"prefix": "165.254.255.0/24",
"group": "groupName",
"group": ["groupName"],
"ignore": false,
"excludeMonitors" : [],
"includeMonitors": []
@@ -270,7 +270,7 @@ describe("Core functions", function() {
"description": "description 3",
"ignoreMorespecifics": true,
"prefix": "192.147.168.0/24",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors" : [],
"includeMonitors": []
@@ -280,7 +280,7 @@ describe("Core functions", function() {
"description": "alarig fix test",
"ignoreMorespecifics": false,
"prefix": "2a00:5884::/32",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors" : [],
"includeMonitors": []
@@ -290,7 +290,7 @@ describe("Core functions", function() {
"description": "alarig fix test 2",
"ignoreMorespecifics": false,
"prefix": "2a0e:f40::/29",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors" : [],
"includeMonitors": []
@@ -300,7 +300,7 @@ describe("Core functions", function() {
"description": "ignore sub test",
"ignoreMorespecifics": true,
"prefix": "2a0e:f40::/30",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors" : [],
"includeMonitors": []
@@ -310,7 +310,7 @@ describe("Core functions", function() {
"description": "ignore flag test",
"ignoreMorespecifics": true,
"prefix": "2a0e:240::/32",
"group": "default",
"group": ["default"],
"ignore": true,
"excludeMonitors" : [],
"includeMonitors": []
@@ -320,7 +320,7 @@ describe("Core functions", function() {
"description": "include exclude test",
"ignoreMorespecifics": false,
"prefix": "175.254.205.0/24",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors" : ["basic-hijack-detection", "withdrawal-detection"],
"includeMonitors": []
@@ -330,7 +330,7 @@ describe("Core functions", function() {
"description": "include exclude test",
"ignoreMorespecifics": false,
"prefix": "170.254.205.0/24",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors" : [],
"includeMonitors": ["prefix-detection"]
@@ -340,7 +340,7 @@ describe("Core functions", function() {
"description": "test fade off",
"ignoreMorespecifics": false,
"prefix": "165.24.225.0/24",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors" : [],
"includeMonitors": []
@@ -350,7 +350,7 @@ describe("Core functions", function() {
"description": "exact matching test",
"ignoreMorespecifics": true,
"prefix": "2001:db8:123::/48",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors" : [],
"includeMonitors": []

View File

@@ -120,7 +120,7 @@ describe("Alerting", function () {
extra: {},
matchedRule: {
prefix: "165.254.255.0/24",
group: "groupName",
group: ["groupName"],
description: "description 2",
asn: [15562],
ignoreMorespecifics: false
@@ -146,7 +146,7 @@ describe("Alerting", function () {
extra: {},
matchedRule:{
prefix:"2a00:5884::/32",
group:"default",
group: ["default"],
description:"alarig fix test",
asn: [204092, 45],
ignoreMorespecifics:false
@@ -172,7 +172,7 @@ describe("Alerting", function () {
extra: {},
matchedRule:{
prefix: "2a00:5884::/32",
group: "default",
group: ["default"],
description: "alarig fix test",
asn: [204092, 45],
ignoreMorespecifics: false
@@ -234,7 +234,7 @@ describe("Alerting", function () {
extra: {},
matchedRule: {
prefix: '175.254.205.0/24',
group: 'default',
group: ['default'],
description: 'include exclude test',
asn: [1234],
ignoreMorespecifics: false,
@@ -262,7 +262,7 @@ describe("Alerting", function () {
extra: {},
matchedRule: {
prefix: '170.254.205.0/24',
group: 'default',
group: ['default'],
description: 'include exclude test',
asn: [1234],
ignoreMorespecifics: false,
@@ -291,7 +291,7 @@ describe("Alerting", function () {
extra: {},
matchedRule: {
prefix: '165.254.255.0/24',
group: 'groupName',
group: ['groupName'],
description: 'description 2',
asn: [15562],
ignoreMorespecifics: false
@@ -317,7 +317,7 @@ describe("Alerting", function () {
extra: {},
matchedRule: {
prefix: '2a00:5884::/32',
group: 'default',
group: ['default'],
description: 'alarig fix test',
asn: [ 204092, 45],
ignoreMorespecifics: false
@@ -386,7 +386,7 @@ describe("Alerting", function () {
"affected": "98.5.4.3/22",
"matchedRule": {
"prefix": "98.5.4.3/22",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors": [],
"includeMonitors": [],
@@ -431,7 +431,7 @@ describe("Alerting", function () {
"affected": "99.5.4.3/22",
"matchedRule": {
"prefix": "99.5.4.3/22",
"group": "default",
"group": ["default"],
"ignore": false,
"excludeMonitors": [],
"includeMonitors": [],

View File

@@ -35,7 +35,7 @@ monitors:
channel: misconfiguration
name: asn-monitor
params:
skipPrefixMatchOnDifferentGroups: false
skipPrefixMatch: false
thresholdMinPeers: 2
- file: monitorRPKI

View File

@@ -70,7 +70,7 @@ describe("Alerting", function () {
"affected": 101,
"matchedRule": {
"asn": [101],
"group": "default",
"group": ["default"],
"groupd": "default",
"upstreams": [100],
"downstreams": [104]
@@ -99,7 +99,7 @@ describe("Alerting", function () {
"affected": 80,
"matchedRule": {
"asn": [80],
"group": "default",
"group": ["default"],
"groupd": "default",
"upstreams": [99],
"downstreams": null
@@ -128,7 +128,7 @@ describe("Alerting", function () {
"affected": 101,
"matchedRule": {
"asn": [101],
"group": "default",
"group": ["default"],
"groupd": "default",
"upstreams": [100],
"downstreams": [104]