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

updated dependencies

This commit is contained in:
Massimo Candela
2020-11-30 17:45:56 +01:00
parent f9c310e45f
commit 0742277952
6 changed files with 204 additions and 79 deletions

24
package-lock.json generated
View File

@@ -3832,9 +3832,9 @@
} }
}, },
"ip-sub": { "ip-sub": {
"version": "1.0.16", "version": "1.0.17",
"resolved": "https://registry.npmjs.org/ip-sub/-/ip-sub-1.0.16.tgz", "resolved": "https://registry.npmjs.org/ip-sub/-/ip-sub-1.0.17.tgz",
"integrity": "sha512-SlF++5+z9wl0oYNVptDBhdXJ/JGSEMrXH+X+/xetMW+kWkWaZQxetzKSzfgnww6CzCCOo7Aoxg5R0eSRi/piXg==", "integrity": "sha512-U/4/OLzZ0TTF7LYog2RbJzXvb/yLWRLo+WDVffRWNnGjqNy0QxVr5u1GEMbUHyQ+dMbe6+vZA3YdefEuC/0xig==",
"requires": { "requires": {
"ip-address": "^6.4.0" "ip-address": "^6.4.0"
} }
@@ -5717,14 +5717,24 @@
} }
}, },
"rpki-validator": { "rpki-validator": {
"version": "2.2.8", "version": "2.2.9",
"resolved": "https://registry.npmjs.org/rpki-validator/-/rpki-validator-2.2.8.tgz", "resolved": "https://registry.npmjs.org/rpki-validator/-/rpki-validator-2.2.9.tgz",
"integrity": "sha512-k2dW4veurdvQhKXJJbST1+T1cGSUe9b9LbUfCykYQuQAbrU3Cb+kVfKEWwN55ZEE0N30C/irYetoSiS7IDWFRw==", "integrity": "sha512-r2aYSvNMbTq1RqzYpvLrJX+pOzIFIKSDAKjDOaNFqWGJ0A1jxBZP2OFIId3cFGcjwJ8LicFiG8tTH+BtF3lo2g==",
"requires": { "requires": {
"axios": "^0.21.0", "axios": "^0.21.0",
"brembo": "^2.0.4", "brembo": "^2.0.4",
"ip-sub": "^1.0.12", "ip-sub": "^1.0.17",
"radix-trie-js": "^1.0.5" "radix-trie-js": "^1.0.5"
},
"dependencies": {
"ip-sub": {
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/ip-sub/-/ip-sub-1.0.17.tgz",
"integrity": "sha512-U/4/OLzZ0TTF7LYog2RbJzXvb/yLWRLo+WDVffRWNnGjqNy0QxVr5u1GEMbUHyQ+dMbe6+vZA3YdefEuC/0xig==",
"requires": {
"ip-address": "^6.4.0"
}
}
} }
}, },
"run-async": { "run-async": {

View File

@@ -49,8 +49,8 @@
"file-stream-rotator": "^0.5.7", "file-stream-rotator": "^0.5.7",
"https-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0",
"inquirer": "^7.3.3", "inquirer": "^7.3.3",
"ip-address": "^6.3.0", "ip-address": "^6.4.0",
"ip-sub": "^1.0.16", "ip-sub": "^1.0.17",
"js-yaml": "^3.14.0", "js-yaml": "^3.14.0",
"kafkajs": "^1.15.0", "kafkajs": "^1.15.0",
"md5": "^2.3.0", "md5": "^2.3.0",
@@ -58,7 +58,7 @@
"nodemailer": "^6.4.16", "nodemailer": "^6.4.16",
"path": "^0.12.7", "path": "^0.12.7",
"restify": "^8.5.1", "restify": "^8.5.1",
"rpki-validator": "^2.2.8", "rpki-validator": "^2.2.9",
"semver": "^7.3.2", "semver": "^7.3.2",
"syslog-client": "^1.1.1", "syslog-client": "^1.1.1",
"ws": "^7.4.0", "ws": "^7.4.0",

View File

@@ -105,6 +105,17 @@ export default class ConnectorTest extends Connector{
case "hijack": case "hijack":
updates = [ updates = [
{
data: {
announcements: [{ // RPKI valid announcement, no alert should be triggered (issue #358)
prefixes: ["193.0.0.0/21"],
next_hop: "1.2.3.4"
}],
peer: "1.2.3.5",
path: [1, 2, 3, 3333]
},
type: "ris_message"
},
{ {
data: { data: {
announcements: [{ announcements: [{

View File

@@ -74,12 +74,10 @@ export default class MonitorHijack extends Monitor {
if (matchedRule && !matchedRule.ignore && !matchedRule.asn.includes(message.originAS)) { if (matchedRule && !matchedRule.ignore && !matchedRule.asn.includes(message.originAS)) {
const origins = [].concat.apply([], [message.originAS.getValue()]); this.rpki.validate(messagePrefix, message.originAS)
Promise .then(result => {
.all(origins.map(asn => this.rpki.validate(messagePrefix, asn, true)))
.then(results => {
if (!results.every(result => result && result.valid)) { if (!result.valid) {
this.publishAlert(message.originAS.getId() + "-" + message.prefix, this.publishAlert(message.originAS.getId() + "-" + message.prefix,
matchedRule.asn.getId(), matchedRule.asn.getId(),
matchedRule, matchedRule,

View File

@@ -1,3 +1,36 @@
/*
* BSD 3-Clause License
*
* Copyright (c) 2019, NTT Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import Monitor from "./monitor"; import Monitor from "./monitor";
export default class MonitorRPKI extends Monitor { export default class MonitorRPKI extends Monitor {
@@ -24,7 +57,8 @@ export default class MonitorRPKI extends Monitor {
this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 1; this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 1;
this.seenRpkiValidAnnouncementsKey = "seen-rpki-valid-announcements"; this.seenRpkiValidAnnouncementsKey = "seen-rpki-valid-announcements";
this.storage
this.storage // Reload the previously discovered ROAs (needed to alert in case of disappearing ROAs)
.get(this.seenRpkiValidAnnouncementsKey) .get(this.seenRpkiValidAnnouncementsKey)
.then(prefixes => { .then(prefixes => {
this.seenRpkiValidAnnouncements = (prefixes) ? prefixes : {}; this.seenRpkiValidAnnouncements = (prefixes) ? prefixes : {};
@@ -35,6 +69,10 @@ export default class MonitorRPKI extends Monitor {
message: error message: error
}); });
}); });
this.queue = [];
setInterval(this._validateBatch, 500); // Periodically validate prefixes-origin pairs
}; };
updateMonitoredResources = () => { updateMonitoredResources = () => {
@@ -66,77 +104,104 @@ export default class MonitorRPKI extends Monitor {
} }
}; };
validate = ({ message, matchedRule} ) => { _validate = (result, message, matchedRule) => {
const prefix = message.prefix; const prefix = result.prefix;
const origin = message.originAS.getValue(); const origin = result.origin.getValue();
return this.rpki if (result) {
.validate(prefix, origin.toString(), true)
.then(result => {
if (result) {
const cacheKey = "a" + [prefix, origin] const cacheKey = "a" + [prefix, origin]
.join("-") .join("-")
.replace(/\./g, "_") .replace(/\./g, "_")
.replace(/\:/g, "_") .replace(/\:/g, "_")
.replace(/\//g, "_"); .replace(/\//g, "_");
const key = `${cacheKey}-${result.valid}`; const key = `${cacheKey}-${result.valid}`;
if (result.valid === null) { if (result.valid === null) {
const cache = this.seenRpkiValidAnnouncements[cacheKey]; const cache = this.seenRpkiValidAnnouncements[cacheKey];
if (cache && cache.rpkiValid && (cache.date + this.cacheValidPrefixesMs) >= new Date().getTime()) { // valid cache if (cache && cache.rpkiValid && (cache.date + this.cacheValidPrefixesMs) >= new Date().getTime()) { // valid cache
this.publishAlert(key, this.publishAlert(key,
prefix, prefix,
matchedRule, matchedRule,
message, message,
{ covering: null, valid: null, roaDisappeared: true }); { covering: null, valid: null, roaDisappeared: true });
} else if (this.params.checkUncovered) { } else if (this.params.checkUncovered) {
this.publishAlert(key, this.publishAlert(key,
prefix, prefix,
matchedRule, matchedRule,
message, message,
{ covering: null, valid: null }); { covering: null, valid: null });
}
} else if (result.valid === false) {
this.publishAlert(key,
prefix,
matchedRule,
message,
{ covering: result.covering, valid: false });
} else if (result.valid) {
// Refresh dictionary
this.seenRpkiValidAnnouncements[cacheKey] = {
date: new Date().getTime(),
rpkiValid: true
};
if (this.seenRpkiValidAnnouncementsTimer) {
clearTimeout(this.seenRpkiValidAnnouncementsTimer);
}
// Store dictionary
this.seenRpkiValidAnnouncementsTimer = setTimeout(() => {
const now = new Date().getTime();
// Delete old cache items
for (let roa of Object.keys(this.seenRpkiValidAnnouncements)) {
if (this.seenRpkiValidAnnouncements[roa].date + this.cacheValidPrefixesMs < now) {
delete this.seenRpkiValidAnnouncements[roa];
} }
} else if (result.valid === false) { }
this.publishAlert(key, this.storage
prefix, .set(this.seenRpkiValidAnnouncementsKey, this.seenRpkiValidAnnouncements)
matchedRule, .catch(error => {
message, this.logger.log({
{ covering: result.covering, valid: false }); level: 'error',
message: error
});
});
}, 1000);
} else if (result.valid) { }
}
};
// Refresh dictionary _validateBatch = () => {
this.seenRpkiValidAnnouncements[cacheKey] = { const batch = {};
date: new Date().getTime(),
rpkiValid: true
};
if (this.seenRpkiValidAnnouncementsTimer) { for (let { message, matchedRule } of this.queue) {
clearTimeout(this.seenRpkiValidAnnouncementsTimer);
}
// Store dictionary const key = message.originAS.getId() + "-" + message.prefix;
this.seenRpkiValidAnnouncementsTimer = setTimeout(() => { batch[key] = batch[key] || [];
const now = new Date().getTime(); batch[key].push({ message, matchedRule });
}
// Delete old cache items this.queue = [];
for (let roa of Object.keys(this.seenRpkiValidAnnouncements)) {
if (this.seenRpkiValidAnnouncements[roa].date + this.cacheValidPrefixesMs < now) {
delete this.seenRpkiValidAnnouncements[roa];
}
}
this.storage
.set(this.seenRpkiValidAnnouncementsKey, this.seenRpkiValidAnnouncements)
.catch(error => {
this.logger.log({
level: 'error',
message: error
});
});
}, 1000);
this.rpki
.validateBatch(Object
.values(batch)
.map((elements) => {
const { message } = elements[0];
return {
prefix: message.prefix,
origin: message.originAS
};
}))
.then(results => {
for (let result of results) {
const key = result.origin.getId() + "-" + result.prefix;
for (let { message, matchedRule } of batch[key]) {
this._validate(result, message, matchedRule);
} }
} }
}) })
@@ -146,7 +211,10 @@ export default class MonitorRPKI extends Monitor {
message: error message: error
}); });
}); });
}
validate = ({ message, matchedRule }) => {
this.queue.push({ message, matchedRule });
}; };
monitor = (message) => { monitor = (message) => {
@@ -155,7 +223,7 @@ export default class MonitorRPKI extends Monitor {
const prefix = message.prefix; const prefix = message.prefix;
const matchedPrefixRule = this.getMoreSpecificMatch(prefix, false); const matchedPrefixRule = this.getMoreSpecificMatch(prefix, false);
if (matchedPrefixRule) { if (matchedPrefixRule && !matchedPrefixRule.ignore) {
this.validate({ message, matchedRule: matchedPrefixRule }); this.validate({ message, matchedRule: matchedPrefixRule });
} else { } else {
const matchedASRule = this.getMonitoredAsMatch(messageOrigin); const matchedASRule = this.getMonitoredAsMatch(messageOrigin);

View File

@@ -189,9 +189,47 @@ export default class RpkiUtils {
}; };
validate = (prefix, origin) => { validate = (prefix, origin) => {
return this.validateBatch([{ prefix, origin }])
.then(results => results[0]);
};
validateBatch = (batch) => {
return this._preCache() return this._preCache()
.then(() => { .then(() => {
return this.rpki.validate(prefix, origin, true); return Promise.all(batch
.map(({ prefix, origin }) => {
const origins = [].concat.apply([], [origin.getValue()]);
return Promise
.all(origins.map(asn => this.rpki.validate(prefix, asn, true))) // Validate each origin
.then(results => {
if (results.length === 1) { // Only one result = only one origin, just return
return { ...results[0], prefix, origin };
} else { // Multiple origin
if (results.every(result => result && result.valid)) { // All valid
return {
valid: true,
covering: [].concat.apply([], results.map(i => i.covering)),
prefix,
origin
};
} else if (results.some(result => result && !result.valid)) { // At least one not valid
return {
valid: false,
covering: [].concat.apply([], results.map(i => i.covering)),
prefix,
origin
};
} else { // return not covered
return {
valid: null,
covering: [].concat.apply([], results.map(i => i.covering)),
prefix,
origin
};
}
}
});
}))
}); });
}; };