From 6635a23b7f7af3735ec7646aa485d4a46c3be50e Mon Sep 17 00:00:00 2001 From: Massimo Candela Date: Fri, 5 Jul 2019 17:06:57 +0200 Subject: [PATCH] introduced input polymorphism and users groups --- config.yml | 49 +++++++--------- consumer.js | 7 ++- env.js | 11 ++-- inputs/input.js | 15 +++++ inputManager.js => inputs/inputYml.js | 34 ++++------- monitors/monitor.js | 17 ++++-- monitors/monitorHijack.js | 11 ++-- reports/report.js | 3 +- reports/reportEmail.js | 82 +++++++++++++++++++-------- reports/reportFile.js | 4 +- 10 files changed, 135 insertions(+), 98 deletions(-) create mode 100644 inputs/input.js rename inputManager.js => inputs/inputYml.js (51%) diff --git a/config.yml b/config.yml index f259968..12bdd25 100644 --- a/config.yml +++ b/config.yml @@ -1,18 +1,16 @@ -# Please, rename this file to config.yml connectors: - file: connectorTest name: tes - - file: connectorRIS - name: ris - params: - url: wss://ris-live.ripe.net/v1/ws/ - moreSpecific: true - type: UPDATE - host: rrc00 - socketOptions: - includeRaw: false - +# - file: connectorRIS +# name: ris +# params: +# url: wss://ris-live.ripe.net/v1/ws/ +# moreSpecific: true +# type: UPDATE +# host: rrc00 +# socketOptions: +# includeRaw: false monitors: - file: monitorHijack @@ -26,38 +24,31 @@ reports: - file: ReportEmail channels: - hijack - - pippo + params: + smtp: smtp.gmail.com + email: sender@email + port: 465 + secure: true + user: user + password: password + notifiedEmails: + default: + - me1@email.it + - me2@email.it -testMode: true checkStaleNotificationsSeconds: 30 notificationIntervalSeconds: 10 clearNotificationQueueAfterSeconds: 10 # If nothing happened in the meanwhile -bufferSize: 10 - # 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: - prefixes.yml - logging: logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated zippedArchive: true maxSize: 20m maxFiles: 14d - - -emailConfig: - enabled: false - smtp: smtp.gmail.com - email: sender@email - port: 465 - secure: true - user: user - password: password - -notifiedEmails: - - me@email.it \ No newline at end of file diff --git a/consumer.js b/consumer.js index ad4cfb2..f5b5e89 100644 --- a/consumer.js +++ b/consumer.js @@ -9,8 +9,11 @@ export default class Consumer { } - this.monitors = env.config.monitors.map(monitor => new monitor.class(monitor.name, monitor.channel, env)); - this.reports = env.config.reports.map(report => new report.class(report.channels, env)); + this.monitors = env.config.monitors + .map(monitor => new monitor.class(monitor.name, monitor.channel, monitor.params, env)); + + this.reports = env.config.reports + .map(report => new report.class(report.channels, report.params, env)); process.on('message', this.dispatch); }; diff --git a/env.js b/env.js index 693bbda..1522105 100644 --- a/env.js +++ b/env.js @@ -3,8 +3,9 @@ import fs from "fs"; import path from "path"; import pubSub from 'pubsub-js'; import winston from 'winston'; -import InputManager from "./inputManager"; +import Input from "./inputs/inputYml"; require('winston-daily-rotate-file'); + const { combine, timestamp, label, printf } = winston.format; const vector = { @@ -69,7 +70,8 @@ config.monitors = (config.monitors || []) return { class: require("./monitors/" + item.file).default, channel: item.channel, - name: item.name + name: item.name, + params: item.params }; }); @@ -78,7 +80,8 @@ config.reports = (config.reports || []) return { class: require("./reports/" + item.file).default, - channels: item.channels + channels: item.channels, + params: item.params }; }); @@ -103,7 +106,7 @@ config.connectors = config.connectors }); -const input = new InputManager(config); +const input = new Input(config); vector.config = config; vector.logger = logger; diff --git a/inputs/input.js b/inputs/input.js new file mode 100644 index 0000000..71dd4b2 --- /dev/null +++ b/inputs/input.js @@ -0,0 +1,15 @@ + +export default class Input { + + constructor(config){ + }; + + getMonitoredMoreSpecifics = () => { + throw new Error('The method getMonitoredMoreSpecifics MUST be implemented'); + }; + + getMonitoredPrefixes = () => { + throw new Error('The method getMonitoredPrefixes MUST be implemented'); + }; + +} \ No newline at end of file diff --git a/inputManager.js b/inputs/inputYml.js similarity index 51% rename from inputManager.js rename to inputs/inputYml.js index 49bb587..5fc91ed 100644 --- a/inputManager.js +++ b/inputs/inputYml.js @@ -1,51 +1,37 @@ import yaml from "js-yaml"; import fs from "fs"; -import ip from "ip"; +import Input from "./input"; - -export default class InputManager { +export default class InputYml extends Input { constructor(config){ + super(config); this.prefixes = []; if (!config.monitoredPrefixesFiles || config.monitoredPrefixesFiles.length === 0){ - throw new Error("The monitoredPrefixesFiles key is missing in the confiug file"); + throw new Error("The monitoredPrefixesFiles key is missing in the config file"); } for (let prefixesFile of config.monitoredPrefixesFiles){ const monitoredPrefixesFile = yaml.safeLoad(fs.readFileSync('./' + prefixesFile, 'utf8')); const monitoredPrefixes = Object.keys(monitoredPrefixesFile) - .map(i => Object.assign(monitoredPrefixesFile[i], { prefix: i })); + .map(i => { + return Object.assign({ + prefix: i, + user: 'default' + }, monitoredPrefixesFile[i]) + }); this.prefixes = this.prefixes.concat(monitoredPrefixes); } }; - getMonitoredMoreSpecifics = () => { return this.prefixes.filter(p => !p.ignoreMorespecifics); }; - getMonitoredMoreSpecificsBest = () => { - const prefixes = this.getMonitoredMoreSpecifics(); - const length = prefixes.length; - let contained = false; - const out = []; - - for (let n=0; n { return this.prefixes; }; diff --git a/monitors/monitor.js b/monitors/monitor.js index 392d349..c07ec71 100644 --- a/monitors/monitor.js +++ b/monitors/monitor.js @@ -1,11 +1,12 @@ export default class Monitor { - constructor(name, channel, env) { + constructor(name, channel, params, env) { this.config = env.config; this.pubSub = env.pubSub; this.logger = env.logger; this.input = env.input; + this.params = params; this.name = name; this.channel = channel; this.monitored = []; @@ -57,22 +58,26 @@ export default class Monitor { affected: firstAlert.affected, message: this.squashAlerts(alerts), data: alerts.map(a => { - return Object.assign(a.data, { + return { + extra: a.extra, + matchedRule: a.matchedRule, + matchedMessage: a.matchedMessage, timestamp: a.timestamp - }); + }; }) } - }; - publishAlert = (id, message, affected, data) => { + publishAlert = (id, message, affected, matchedRule, matchedMessage, extra) => { const context = { id, timestamp: new Date().getTime(), message, affected, - data + matchedRule, + matchedMessage, + extra }; if (!this.alerts[id]) { diff --git a/monitors/monitorHijack.js b/monitors/monitorHijack.js index 38623b2..b114a39 100644 --- a/monitors/monitorHijack.js +++ b/monitors/monitorHijack.js @@ -4,8 +4,8 @@ import ip from "ip"; export default class MonitorHijack extends Monitor { - constructor(name, channel, env){ - super(name, channel, env); + constructor(name, channel, params, env){ + super(name, channel, params, env); }; updateMonitoredPrefixes = () => { @@ -37,10 +37,9 @@ export default class MonitorHijack extends Monitor { this.publishAlert(message.originAs + "-" + match.prefix, `The prefix ${match.prefix} is announced by the AS${message.originAs} instead of AS${match.asn}`, match.asn, - { - rule: matches[0], - received: message - }); + matches[0], + message, + {}); } resolve(true); diff --git a/reports/report.js b/reports/report.js index 06f0833..0d8f8a2 100644 --- a/reports/report.js +++ b/reports/report.js @@ -1,11 +1,12 @@ export default class Report { - constructor(channels, env) { + constructor(channels, params, env) { this.config = env.config; this.logger = env.logger; this.pubSub = env.pubSub; + this.params = params; for (let channel of channels){ env.pubSub.subscribe(channel, (message, content) => { diff --git a/reports/reportEmail.js b/reports/reportEmail.js index c449b3e..8056e66 100644 --- a/reports/reportEmail.js +++ b/reports/reportEmail.js @@ -3,38 +3,72 @@ import nodemailer from "nodemailer"; export default class ReportEmail extends Report { - constructor(channels, env) { - super(channels, env); + constructor(channels,params, env) { + super(channels, params, env); - if (this.config.emailConfig.enabled) { - this.transporter = nodemailer.createTransport({ - host: this.config.emailConfig.smtp, - port: this.config.emailConfig.port, - secure: this.config.emailConfig.secure, - auth: { - user: this.config.emailConfig.user, - pass: this.config.emailConfig.password + this.transporter = nodemailer.createTransport({ + host: this.params.smtp, + port: this.params.port, + secure: this.params.secure, + auth: { + user: this.params.user, + pass: this.params.password + } + }); + } + + getEmails = (content) => { + const users = content.data + .map(item => { + if (item.matchedRule && item.matchedRule.user){ + return item.matchedRule.user; + } else { + return false; } + }) + .filter(item => !!item); + + try { + return [...new Set(users)] + .map(user => { + return this.params.notifiedEmails[user]; + }); + } catch (error) { + this.logger.log({ + level: 'error', + message: 'Not all users have an associated email address' }); } - } + + return []; + }; + + getEmailText = (message, content) => { + return content.message; + }; report = (message, content) => { if (this.transporter) { - this.transporter - .sendMail({ - from: this.config.emailConfig.email, - to: this.config.notifiedEmails.join(', '), - subject: "BGP alert: " + message, - text: "Hello world?" - }) - .catch(error => { - this.logger.log({ - level: 'error', - message: error - }); - }) + const emailGroups = this.getEmails(content); + + for (let emails of emailGroups) { + console.log(content.message); + + this.transporter + .sendMail({ + from: this.params.email, + to: emails.join(', '), + subject: 'BGP alert: ' + message, + text: this.getEmailText(message, content) + }) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }) + } } } } \ No newline at end of file diff --git a/reports/reportFile.js b/reports/reportFile.js index 4ef9d0d..bc46226 100644 --- a/reports/reportFile.js +++ b/reports/reportFile.js @@ -3,8 +3,8 @@ import nodemailer from "nodemailer"; export default class ReportEmail extends Report { - constructor(channels, env) { - super(channels, env); + constructor(channels, params, env) { + super(channels, params, env); } report = (message, content) => {