From 7881a68dc898a891979c50a1a9916d9bdb607591 Mon Sep 17 00:00:00 2001 From: curtisz Date: Thu, 22 Jan 2015 16:41:12 -0800 Subject: [PATCH] added: SSL support for API/monitor interface --- README.md | 15 +++++++++++++++ app.js | 19 +++++++++++++++++-- config/default.yaml | 6 ++++++ lib/monitor.js | 13 ++++++++++--- monitor.js | 3 ++- 5 files changed, 50 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b1c9cfa48..73eea3b75 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,21 @@ url: 'http://myDomain.com' Node that Uptime works great behind a proxy - it uses the `http_proxy` environment variable transparently. +SSL +--- + +By default, Uptime uses regular HTTP on the API and monitor server, but it's possible to enable SSL for encrypting the connection to your monitor instance. The settings for this are located in the `config/default.yaml` file: + +```yaml +ssl: + enabled: true + certificate: uptime.crt # path to certificate file + key: uptime.key # path to key file + selfSigned: false +``` + +You must specify `true` for the `selfSigned` option when using a self-signed certificate, otherwise Node.js will throw an "UNABLE_TO_VERIFY_LEAF_NODE" error and will not poll. + Architecture ------------ diff --git a/app.js b/app.js index 6e0714f51..2a77ba8e8 100644 --- a/app.js +++ b/app.js @@ -3,6 +3,7 @@ */ var http = require('http'); +var https = require('https'); var url = require('url'); var express = require('express'); var config = require('config'); @@ -26,7 +27,21 @@ a.start(); // web front var app = module.exports = express(); -var server = http.createServer(app); +if (config.ssl && config.ssl.enabled === true) { + if (typeof(config.ssl.certificate) === 'undefined') { + throw new Error("Must specify certificate to enable SSL!"); + } + if (typeof(config.ssl.key) === 'undefined') { + throw new Error("Must specify key file to enable SSL!"); + } + var options = { + cert: fs.readFileSync(config.ssl.certificate), + key: fs.readFileSync(config.ssl.key) + }; + var server = https.createServer(options, app); +} else { + var server = http.createServer(app); +} app.configure(function(){ app.use(app.router); @@ -149,7 +164,7 @@ if (!module.parent) { } else { port = serverUrl.port; if (port === null) { - port = 80; + port = config.ssl && config.ssl.enabled ? 443 : 80; } } var port = process.env.PORT || port; diff --git a/config/default.yaml b/config/default.yaml index 7bc4a0714..3c0d9461f 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -14,6 +14,12 @@ monitor: timeout: 5000 # five seconds userAgent: NodeUptime/3.0 (https://github.com/fzaninotto/uptime) +ssl: + enabled: false + certificate: uptime.crt # certificate file + key: uptime.key # key file + selfSigned: false + analyzer: updateInterval: 60000 # one minute qosAggregationInterval: 600000 # ten minutes diff --git a/lib/monitor.js b/lib/monitor.js index c9f7abd97..b18d29e3a 100644 --- a/lib/monitor.js +++ b/lib/monitor.js @@ -2,6 +2,7 @@ * Module dependencies. */ var http = require('http'); +var https = require('https'); var url = require('url'); var EventEmitter = require('events').EventEmitter; var PollerCollection = require('./pollers/pollerCollection'); @@ -26,6 +27,9 @@ function Monitor(config) { config.timeout = config.timeout || 5 * 1000; this.config = config; this.pollerCollection = new PollerCollection(); + this.selfSigned = config.selfSigned; + this.ssl = url.parse(config.apiUrl).protocol.indexOf('https') > -1 ? true : false; + this.connection = this.ssl === true ? https : http; this.apiHttpOptions = {}; } @@ -87,7 +91,8 @@ Monitor.prototype.findChecksNeedingPoll = function(callback) { var options = url.parse(this.config.apiUrl + '/checks/needingPoll'); this.applyApiHttpOptions(options); var self = this; - http.get(options, function(res) { + if (self.selfSigned === true) options.rejectUnauthorized = false; + this.connection.get(options, function(res) { if (res.statusCode != 200) { return callback(new Error(self.config.apiUrl + '/checks/needingPoll resource responded with error code: ' + res.statusCode)); } @@ -151,7 +156,8 @@ Monitor.prototype.declarePoll = function(check, callback) { options.method = 'PUT'; this.applyApiHttpOptions(options); var self = this; - var req = http.request(options, function(res) { + if (self.selfSigned === true) options.rejectUnauthorized = false; + var req = this.connection.request(options, function(res) { if (res.statusCode != 200) { return callback(new Error(self.config.apiUrl + '/check/:id/test resource responded with error code: ' + res.statusCode)); } @@ -187,7 +193,8 @@ Monitor.prototype.createPing = function(error, check, timestamp, time, details, }; this.applyApiHttpOptions(options); var self = this; - var req = http.request(options, function(res) { + if (self.selfSigned === true) options.rejectUnauthorized = false; + var req = this.connection.request(options, function(res) { if (res.statusCode != 200) { return callback(new Error(self.config.apiUrl + '/pings resource responded with error code: ' + res.statusCode)); } diff --git a/monitor.js b/monitor.js index af9b929bb..67e60fa09 100644 --- a/monitor.js +++ b/monitor.js @@ -3,6 +3,7 @@ var config = require('config'); var Monitor = require('./lib/monitor'); // start the monitor +config.monitor.selfSigned = config.ssl && config.ssl.selfSigned ? true : false; monitor = Monitor.createMonitor(config.monitor); // load plugins @@ -18,4 +19,4 @@ config.plugins.forEach(function(pluginName) { monitor.start(); -module.exports = monitor; \ No newline at end of file +module.exports = monitor;