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

introduced tests + refactoring

This commit is contained in:
Massimo Candela
2019-07-09 02:46:08 +02:00
parent 9b7de79264
commit 4319fbfea6
21 changed files with 1161 additions and 236 deletions

View File

@@ -1,13 +1,11 @@
environment: production
connectors:
- file: connectorTest
name: tes
params:
testType: withdrawal
- file: connectorRIS
name: ris
params:
url: wss://ris-live.ripe.net/v1/ws/
subscription:
moreSpecific: true
type: UPDATE
host: rrc00
@@ -18,12 +16,16 @@ monitors:
- file: monitorHijack
channel: hijack
name: basic-hijack-detection
- file: monitorNewPrefix
channel: newprefix
name: prefix-detection
- file: monitorVisibility
channel: visibility
name: withdrawal-detection
params:
threshold: 0
reports:
- file: reportFile
@@ -31,26 +33,17 @@ reports:
- hijack
- newprefix
- visibility
-
- file: ReportEmail
channels:
- hijack
- newprefix
- visibility
params:
smtp: smtp.gmail.com
email: sender@email
port: 465
secure: true
user: user
password: password
notifiedEmails:
default:
- me1@email.it
- me2@email.it
checkStaleNotificationsSeconds: 30
notificationIntervalSeconds: 10
clearNotificationQueueAfterSeconds: 10 # If nothing happened in the meanwhile
checkStaleNotificationsSeconds: 60
notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds
clearNotificationQueueAfterSeconds: 1900 # Stop with the alert for an event which didn't happen again in x seconds
# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example
# This is an array (use new lines and dashes!)
@@ -58,6 +51,7 @@ monitoredPrefixesFiles:
- prefixes.yml
logging:
directory: logs
logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated
zippedArchive: true
maxSize: 20m

View File

@@ -59,10 +59,11 @@ export default class ConnectorFactory {
if (connectors.length === 0) {
reject(new Error("No connections available"));
} else {
resolve(Promise.all(connectors.map(connector => {
connector.subscribe(params);
})));
const connectorList = connectors
.map(connector => connector.subscribe(params));
resolve(Promise.all(connectorList));
}
});
})
}

View File

@@ -16,10 +16,10 @@ export default class Connector {
new Promise((resolve, reject) => reject(new Error('The method connect MUST be implemented')));
error = () => {
error = (error) => {
this.logger.log({
level: 'error',
message: this.name + ' disconnected'
message: error
});
};

View File

@@ -21,6 +21,7 @@ export default class ConnectorRIS extends Connector{
});
} catch(error) {
this.error(error);
resolve(false);
}
});
@@ -31,10 +32,11 @@ export default class ConnectorRIS extends Connector{
try {
this.ws.send(JSON.stringify({
type: "ris_subscribe",
data: this.params
data: this.params.subscription
}));
resolve(true);
} catch(error) {
this.error(error);
resolve(false);
}
});
@@ -48,11 +50,11 @@ export default class ConnectorRIS extends Connector{
const peer = message["peer"];
const path = message["path"];
for (let announcement of announcements){
for (let announcement of announcements) {
const nextHop = announcement["next_hop"];
const prefixes = announcement["prefixes"] || [];
for (let prefix of prefixes){
for (let prefix of prefixes) {
components.push({
type: "announcement",
prefix,
@@ -64,7 +66,7 @@ export default class ConnectorRIS extends Connector{
}
}
for (let prefix of withdrawals){
for (let prefix of withdrawals) {
components.push({
type: "withdrawal",
prefix,
@@ -74,5 +76,5 @@ export default class ConnectorRIS extends Connector{
return components;
}
};
}
}
};

View File

@@ -1,10 +1,12 @@
import sleep from "sleep";
import Connector from "./connector";
export default class ConnectorTest extends Connector{
static isTest = true;
constructor(name, params, env) {
super(name, params, env);
console.log("Test connector running");
}
connect = () =>
@@ -12,7 +14,7 @@ export default class ConnectorTest extends Connector{
resolve(true);
});
subscribe = (input) =>
subscribe = () =>
new Promise((resolve, reject) => {
resolve(true);
@@ -36,12 +38,12 @@ export default class ConnectorTest extends Connector{
type: "ris_message"
};
const message = JSON.stringify(update);
while (true){
this.message(message);
sleep.sleep(1);
}
setInterval(() => {
this.message(JSON.stringify(update));
let peer = update.data.peer.split('.');
peer[3] = Math.min(parseInt(peer[3]) + 1, 254);
update.data.peer = peer.join(".");
}, 1000);
});

View File

@@ -8,7 +8,6 @@ export default class Consumer {
this.connectors[connector.name] = connector.class
}
this.monitors = env.config.monitors
.map(monitor => new monitor.class(monitor.name, monitor.channel, monitor.params, env));
@@ -16,6 +15,9 @@ export default class Consumer {
.map(report => new report.class(report.channels, report.params, env));
process.on('message', this.dispatch);
env.pubSub.subscribe('data', (type, data) => {
this.dispatch(data);
});
};
dispatch = (data) => {

21
env.js
View File

@@ -8,16 +8,16 @@ require('winston-daily-rotate-file');
const { combine, timestamp, label, printf } = winston.format;
const vector = {
env: "dev" // Get the real one later
};
const vector = {};
const fileName = process.argv[2] || path.resolve(__dirname, 'config.yml');
console.log("loading config:", fileName);
const config = yaml.safeLoad(fs.readFileSync(fileName, 'utf8'));
const config = yaml.safeLoad(fs.readFileSync(path.resolve(__dirname, 'config.yml'), 'utf8'));
const formatLine = printf(({ level, message, label, timestamp }) => `${timestamp} [${label}] ${level}: ${message}`);
const verboseFilter = winston.format((info, opts) => info.level === 'verbose' ? info : false);
const transportError = new (winston.transports.DailyRotateFile)({
filename: 'logs/error-%DATE%.log',
filename: config.logging.directory +'/error-%DATE%.log',
datePattern: config.logging.logRotatePattern,
zippedArchive: config.logging.zippedArchive,
maxSize: config.logging.maxSize,
@@ -27,13 +27,13 @@ const transportError = new (winston.transports.DailyRotateFile)({
eol: '\n',
json: false,
format: combine(
label({ label: vector.env}),
label({ label: config.environment}),
timestamp(),
formatLine
)
});
const transportReports = new (winston.transports.DailyRotateFile)({
filename: 'logs/reports-%DATE%.log',
filename: config.logging.directory + '/reports-%DATE%.log',
datePattern: config.logging.logRotatePattern,
zippedArchive: config.logging.zippedArchive,
maxSize: config.logging.maxSize,
@@ -44,7 +44,7 @@ const transportReports = new (winston.transports.DailyRotateFile)({
json: false,
format: combine(
verboseFilter(),
label({ label: vector.env}),
label({ label: config.environment}),
timestamp(),
formatLine
)
@@ -61,7 +61,7 @@ const logger = winston.createLogger({
]
});
if (vector.env === 'prod') {
if (config.environment === 'production') {
logger.remove(logger.transports.Console);
}
@@ -88,7 +88,7 @@ config.reports = (config.reports || [])
config.connectors = config.connectors || [];
if ([...new Set(config.connectors)].length !== config.connectors.length) {
throw new Error('Connectors names MUST to be unique');
throw new Error('Connectors names MUST be unique');
}
config.connectors = config.connectors
@@ -106,6 +106,7 @@ config.connectors = config.connectors
});
const input = new Input(config);
vector.config = config;

View File

@@ -1,23 +1,29 @@
import { config, logger, input } from "./env";
import cluster from "cluster";
import { config, logger, input, pubSub } from "./env";
import Consumer from "./consumer";
import ConnectorFactory from "./connectorFactory";
import cluster from "cluster";
if (cluster.isMaster) {
const worker = cluster.fork();
function master(worker) {
const connectorFactory = new ConnectorFactory();
connectorFactory.loadConnectors();
connectorFactory.connectConnectors()
return connectorFactory.connectConnectors()
.then(() => {
for (const connector of connectorFactory.getConnectors()) {
if (worker){
connector.onMessage((message) => {
worker.send(connector.name + "-" + message);
});
} else {
connector.onMessage((message) => {
pubSub.publish("data", connector.name + "-" + message);
});
}
connector.onError(error => {
logger.log({
level: 'error',
@@ -39,8 +45,20 @@ if (cluster.isMaster) {
message: error
});
});
}
module.exports = pubSub;
console.log("RUNNING ENVIRONMENT:", config.environment);
if (config.environment === "test") {
master();
new Consumer();
} else {
if (cluster.isMaster) {
master(cluster.fork());
} else {
new Consumer();
}
}

File diff suppressed because one or more lines are too long

View File

@@ -17,7 +17,7 @@ const verboseFilter = winston.format((info, opts) => {
const transportError = new (winston.transports.DailyRotateFile)({
filename: 'logs/error-%DATE%.log',
filename: config.logging.directory + '/error-%DATE%.log',
datePattern: config.logging.logRotatePattern,
zippedArchive: config.logging.zippedArchive,
maxSize: config.logging.maxSize,
@@ -34,7 +34,7 @@ const transportError = new (winston.transports.DailyRotateFile)({
});
const transportReports = new (winston.transports.DailyRotateFile)({
filename: 'logs/reports-%DATE%.log',
filename: config.logging.directory + '/reports-%DATE%.log',
datePattern: config.logging.logRotatePattern,
zippedArchive: config.logging.zippedArchive,
maxSize: config.logging.maxSize,

View File

@@ -35,12 +35,15 @@ export default class Monitor {
_squash = (alerts) => {
const message = this.squashAlerts(alerts);
if (message) {
const firstAlert = alerts[0];
const id = firstAlert.id;
let earliest = Infinity;
let latest = -Infinity;
for (let alert of alerts){
for (let alert of alerts) {
earliest = Math.min(alert.timestamp, earliest);
latest = Math.max(alert.timestamp, latest);
@@ -56,7 +59,7 @@ export default class Monitor {
earliest,
latest,
affected: firstAlert.affected,
message: this.squashAlerts(alerts),
message,
data: alerts.map(a => {
return {
extra: a.extra,
@@ -66,6 +69,7 @@ export default class Monitor {
};
})
}
}
};
publishAlert = (id, message, affected, matchedRule, matchedMessage, extra) => {
@@ -122,6 +126,7 @@ export default class Monitor {
for (let id in this.alerts) {
const group = this._squash(this.alerts[id]);
if (group) {
if (this._checkLastSent(group)) {
this.sent[group.id] = new Date().getTime();
this._publishOnChannel(group);
@@ -129,6 +134,7 @@ export default class Monitor {
this._clean(group);
}
}
};

View File

@@ -5,6 +5,7 @@ export default class MonitorVisibility extends Monitor {
constructor(name, channel, params, env){
super(name, channel, params, env);
this.threshold = (params.threshold != null) ? params.threshold : 10;
};
updateMonitoredPrefixes = () => {
@@ -17,7 +18,14 @@ export default class MonitorVisibility extends Monitor {
squashAlerts = (alerts) => {
const peers = [...new Set(alerts.map(alert => alert.matchedMessage.peer))].length;
return `The prefix ${alerts[0].matchedMessage.prefix} has been withdrawn. It is no longer visible from ${peers} peer(s).`;
if (peers >= this.threshold) {
return (peers === 1) ?
`The prefix ${alerts[0].matchedMessage.prefix} it's no longer visible (withdrawn) from the peer ${alerts[0].matchedMessage.peer}.` :
`The prefix ${alerts[0].matchedMessage.prefix} has been withdrawn. It is no longer visible from ${peers} peers.`;
} else {
return false;
}
};
monitor = (message) =>
@@ -34,7 +42,9 @@ export default class MonitorVisibility extends Monitor {
if (matches.length !== 0) {
const match = matches[0];
this.publishAlert(match.prefix,
let key = match.prefix;
this.publishAlert(key,
`The prefix ${match.prefix} has been withdrawn.`,
match.asn,
matches[0],

View File

@@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "./node_modules/.bin/mocha tests --require babel-core/register",
"build": "",
"watch-and-serve": "nodemon --inspect --exec babel-node index.js",
"serve": "nodemon --exec babel-node index.js"
@@ -13,7 +13,15 @@
"license": "ISC",
"devDependencies": {
"babel-cli": "^6.26.0",
"nodemon": "^1.19.1"
"babel-core": "^6.26.3",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-env": "^1.7.0",
"chai": "^4.2.0",
"chai-subset": "^1.6.0",
"mocha": "^6.1.4",
"nodemon": "^1.19.1",
"read-last-lines": "^1.7.1"
},
"dependencies": {
"babel-core": "^6.26.3",

View File

@@ -1,37 +1,37 @@
27.114.0.0/17:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
58.88.0.0/13:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
60.32.0.0/12:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
61.112.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
61.113.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
61.118.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
61.119.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
61.120.144.0/20:
description: subas 64600
@@ -40,12 +40,12 @@
61.126.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
61.199.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
61.200.80.0/20:
description: subas 64600
@@ -54,12 +54,12 @@
61.207.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
61.208.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
61.213.144.0/20:
description: subas 64600
@@ -88,12 +88,12 @@
114.144.0.0/12:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
114.160.0.0/11:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
117.103.176.0/20:
description: subas 64600
@@ -110,12 +110,12 @@
118.0.0.0/12:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
118.16.0.0/13:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
120.29.144.0/21:
description: subas 64600
@@ -128,37 +128,37 @@
121.112.0.0/13:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
122.1.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
122.16.0.0/12:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
123.216.0.0/13:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
123.224.0.0/14:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
124.40.0.0/18:
description: subas 64600
asn: 2914
ignoreMorespecifics: False
ignoreMorespecifics: false
124.40.32.0/19:
description: iboss
asn: 137922
ignoreMorespecifics: True
ignoreMorespecifics: true
124.40.56.0/24:
description: capitalonline
@@ -171,27 +171,27 @@
124.84.0.0/14:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
124.96.0.0/13:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
125.170.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
125.172.0.0/14:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
125.200.0.0/13:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
130.94.19.0/24:
description: subas 65061
@@ -208,32 +208,32 @@
153.176.0.0/12:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
153.192.0.0/11:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
153.224.0.0/12:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
153.240.0.0/13:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
153.248.0.0/14:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
153.252.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
153.254.0.0/15:
description: subas 64600
@@ -242,37 +242,37 @@
180.0.0.0/10:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.12.169.0/24:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.12.168.0/24:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.12.170.0/24:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.12.171.0/24:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.12.172.0/24:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.12.173.0/24:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.210.232.0/24:
description: subas 64600
@@ -293,7 +293,7 @@
202.234.192.0/18:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
203.105.64.0/19:
description: subas 64600
@@ -302,17 +302,17 @@
203.139.160.0/19:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
203.140.96.0/20:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
203.140.112.0/20:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
203.205.112.0/20:
description: subas 64600
@@ -353,47 +353,47 @@
210.132.0.0/18:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.145.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.154.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.160.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.161.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.162.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.163.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.164.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.170.64.0/18:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.175.160.0/19:
description: subas 64600
@@ -402,57 +402,57 @@
210.190.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.225.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.226.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.232.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.248.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
210.254.128.0/17:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
211.0.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
211.6.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
211.11.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
211.16.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
211.122.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
211.129.0.0/16:
description: OCN
@@ -461,7 +461,7 @@
211.130.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
211.130.96.0/19:
description: subas 64600
@@ -474,204 +474,204 @@
218.43.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
218.44.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
218.47.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
218.224.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
218.230.128.0/17:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
219.96.128.0/17:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
219.114.0.0/17:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
219.160.0.0/14:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
219.164.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
219.166.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.96.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.98.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.99.0.0/17:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.99.128.0/17:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.104.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.106.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.108.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.110.0.0/16:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.111.0.0/19:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.111.32.0/20:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.111.48.0/20:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.111.64.0/18:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.111.128.0/17:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.220.0.0/15:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
221.113.128.0/17:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
221.184.0.0/13:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
222.144.0.0/13:
description: OCN
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
223.216.0.0/14:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
114.163.192.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
124.85.96.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.0.248.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.13.216.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.22.248.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.3.96.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.39.216.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.50.120.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.57.120.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
180.57.128.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
219.162.32.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
220.96.8.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: False
ignoreMorespecifics: false
124.40.52.128/26:
description: Solid Trading / Crossivity
asn: 50601
ignoreMorespecifics: False
ignoreMorespecifics: false

View File

@@ -10,7 +10,7 @@ export default class Report {
for (let channel of channels){
env.pubSub.subscribe(channel, (message, content) => {
this.report(message, content);
return this.report(message, content);
});
}
}

View File

@@ -9,10 +9,15 @@ export default class ReportEmail extends Report {
this.transporter = nodemailer.createTransport({
host: this.params.smtp,
port: this.params.port,
secure: this.params.secure,
secure: this.params.useTls,
ignoreTLS: this.params.ignoreTLS,
auth: {
user: this.params.user,
pass: this.params.password
pass: this.params.password,
type: this.params.authType
},
tls: {
rejectUnauthorizedCertificate: this.params.rejectUnauthorizedCertificate
}
});
}
@@ -53,7 +58,6 @@ export default class ReportEmail extends Report {
const emailGroups = this.getEmails(content);
for (let emails of emailGroups) {
console.log(content.message);
this.transporter
.sendMail({

View File

@@ -1,5 +1,4 @@
import Report from "./report";
import nodemailer from "nodemailer";
export default class ReportEmail extends Report {

45
tests/config.test.yml Normal file
View File

@@ -0,0 +1,45 @@
environment: test
connectors:
- file: connectorTest
name: tes
params:
testType: withdrawal
monitors:
- file: monitorHijack
channel: hijack
name: basic-hijack-detection
- file: monitorNewPrefix
channel: newprefix
name: prefix-detection
- file: monitorVisibility
channel: visibility
name: withdrawal-detection
params:
threshold: 4
reports:
- file: reportFile
channels:
- hijack
- newprefix
- visibility
checkStaleNotificationsSeconds: 60
notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds
clearNotificationQueueAfterSeconds: 1900 # Stop with the alert for an event which didn't happen again in x seconds
# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example
# This is an array (use new lines and dashes!)
monitoredPrefixesFiles:
- tests/prefixes.test.yml
logging:
directory: logs
logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated
zippedArchive: true
maxSize: 20m
maxFiles: 14d

14
tests/prefixes.test.yml Normal file
View File

@@ -0,0 +1,14 @@
124.40.52.128/26:
description: Solid Trading / Crossivity
asn: 50601
ignoreMorespecifics: false
180.50.120.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: false
180.57.120.0/21:
description: OCN prefix
asn: 4713
ignoreMorespecifics: true

205
tests/test.js Normal file
View File

@@ -0,0 +1,205 @@
var chai = require("chai");
var chaiSubset = require('chai-subset');
var readLastLines = require('read-last-lines');
var moment = require('moment');
chai.use(chaiSubset);
var expect = chai.expect;
describe("Configuration loader", function() {
process.argv[2] = "tests/config.test.yml";
var env = require("../env");
it("config structure", function() {
expect(env.config).to.have
.keys([
"environment",
"connectors",
"monitors",
"reports",
"checkStaleNotificationsSeconds",
"notificationIntervalSeconds",
"clearNotificationQueueAfterSeconds",
"monitoredPrefixesFiles",
"logging"
]);
expect(env.config.connectors[0]).to.have
.property('class')
});
it("loading connectors", function() {
expect(env.config.connectors[0]).to
.containSubset({
"params": { "testType" : "withdrawal" },
"name": "tes"
});
expect(env.config.connectors[0]).to.have
.property('class')
});
it("loading monitors", function() {
expect(env.config.monitors[0]).to
.containSubset({
"channel": "hijack",
"name": "basic-hijack-detection",
"params": undefined
});
expect(env.config.monitors[1]).to
.containSubset({
"channel": "newprefix",
"name": "prefix-detection",
"params": undefined
});
expect(env.config.monitors[2]).to
.containSubset({
"channel": "visibility",
"name": "withdrawal-detection",
"params": {
"threshold": 4
}
});
expect(env.config.monitors[0]).to.have
.property('class')
});
it("loading reports", function() {
expect(env.config.reports[0]).to
.containSubset({
"channels": [
"hijack",
"newprefix",
"visibility"
],
"params": undefined
});
expect(env.config.reports[0]).to.have
.property('class')
});
});
describe("Input loader", function() {
process.argv[2] = "tests/config.test.yml";
var env = require("../env");
it("loading prefixes", function() {
expect(env.input).to
.containSubset({
"prefixes": [
{
"asn": 50601,
"description": "Solid Trading / Crossivity",
"ignoreMorespecifics": false,
"prefix": "124.40.52.128/26",
"user": "default",
},
{
"asn": 4713,
"description": "OCN prefix",
"ignoreMorespecifics": false,
"prefix": "180.50.120.0/21",
"user": "default",
},
{
"asn": 4713,
"description": "OCN prefix",
"ignoreMorespecifics": true,
"prefix": "180.57.120.0/21",
"user": "default",
}
]
});
});
});
describe("Logging", function() {
process.argv[2] = "tests/config.test.yml";
var env = require("../env");
it("Errors logging on the right file", function(done) {
const message = "Test message";
env.logger
.log({
level: "error",
message: message
});
const file = env.config.logging.directory + "/error-" + moment().format('YYYY-MM-DD') + ".log";
readLastLines
.read(file, 1)
.then((line) => {
const lineMessage = line.split(" ").slice(3, 5).join(" ").trim();
expect(lineMessage).to
.equal(message);
done();
});
});
it("Reports logging on the right file", function(done) {
const message = "Test message";
env.logger
.log({
level: "verbose",
message: message
});
const file = env.config.logging.directory + "/reports-" + moment().format('YYYY-MM-DD') + ".log";
readLastLines
.read(file, 1)
.then((line) => {
const lineMessage = line.split(" ").slice(3, 5).join(" ").trim();
expect(lineMessage).to
.equal(message);
done();
});
});
});
describe("Alerting", function() {
process.argv[2] = "tests/config.test.yml";
var pubSub = require("../index");
var env = require("../env");
it("Data analyzed in time (from boot)", function(done) {
pubSub.subscribe("visibility", function (type, message) {
expect(message).to
.containSubset({
id: '124.40.52.128/26',
origin: 'withdrawal-detection',
affected: 50601,
message: 'The prefix 124.40.52.128/26 has been withdrawn. It is no longer visible from 4 peers.'
});
expect(message).to.contain
.keys([
"latest",
"earliest",
"data"
]);
done();
process.exit()
});
}).timeout(5000);
});

610
yarn.lock

File diff suppressed because it is too large Load Diff