From b3d0896f5197056fb0d9549ef44be349178d670f Mon Sep 17 00:00:00 2001 From: rxbn_ Date: Sat, 26 Sep 2020 22:23:52 +0200 Subject: [PATCH] web,prometheus - initial support --- README.md | 4 +- app.js | 2 +- bin/config.js | 4 ++ bin/prometheus/module.js | 63 ++++++++++++++++++++++++++++++++ bin/web/routes/api/prometheus.js | 44 ++++++++++++++++++++++ package.json | 1 + 6 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 bin/prometheus/module.js create mode 100644 bin/web/routes/api/prometheus.js diff --git a/README.md b/README.md index 9e1fbf2..41056cd 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ Single sign-on authentication and authorization service for rxbn.de services # start server ## regular ```sh -$ DB_URL="mongodb://user:pass@ip:port/authdb" DB_NAME="authRxbn" SESSION_KEY="32byteHexString" COOKIE_KEY="32byteHexString" node app.js +$ DB_URL="mongodb://user:pass@ip:port/authdb" DB_NAME="authRxbn" SESSION_KEY="32byteHexString" COOKIE_KEY="32byteHexString" PROMETHEUS_USER="authUsername" PROMETHEUS_PW="authPassword" node app.js ``` ## debug ```sh -$ DB_URL="mongodb://user:pass@ip:port/authdb" DB_NAME="authRxbn" SESSION_KEY="32byteHexString" COOKIE_KEY="32byteHexString" NODE_ENV=debug node app.js +$ DB_URL="mongodb://user:pass@ip:port/authdb" DB_NAME="authRxbn" SESSION_KEY="32byteHexString" COOKIE_KEY="32byteHexString" PROMETHEUS_USER="authUsername" PROMETHEUS_PW="authPassword" NODE_ENV=debug node app.js ``` diff --git a/app.js b/app.js index d6a9ede..36567fc 100644 --- a/app.js +++ b/app.js @@ -16,7 +16,7 @@ let load = global['requireModule'] = (name) => { }; // environment variable check -let env_vars = ["DB_URL", "DB_NAME", "SESSION_KEY", "COOKIE_KEY"]; +let env_vars = ["DB_URL", "DB_NAME", "SESSION_KEY", "COOKIE_KEY", "PROMETHEUS_USER", "PROMETHEUS_PW"]; let env_missing = false; env_vars.forEach((el) => { if(typeof process.env[el] == 'undefined') { diff --git a/bin/config.js b/bin/config.js index 95de709..539ad8e 100644 --- a/bin/config.js +++ b/bin/config.js @@ -34,5 +34,9 @@ module.exports = { mongoose: { uri: process.env.DB_URL, db: process.env.DB_NAME + }, + prometheus: { + auth_user: process.env.PROMETHEUS_USER, + auth_pass: process.env.PROMETHEUS_PW } }; diff --git a/bin/prometheus/module.js b/bin/prometheus/module.js new file mode 100644 index 0000000..2836321 --- /dev/null +++ b/bin/prometheus/module.js @@ -0,0 +1,63 @@ +/* + * This file is part of the authRXBN single sign-on package. + * + * (c) Ruben Meyer + */ + +// init +var methods = {}; + +let client = require('prom-client'); +client.collectDefaultMetrics(); // collect system info + +let register = client.register; + +let metrics = {}; + +var cfg = require(global['__dirname']+'/bin/config'); + +/** + * get a metric + * @author Ruben Meyer + * @param {String} name metric name + * @return {Metric} + */ +methods.getMetric = (name) => { + return metrics[name]; +} + +/** + * add a metric + * @author Ruben Meyer + * @param {String} name metric name + * @param {Metric} metric metric object + * @param {Boolean} overwrite overwrite parameter to overwrite metric if it already exists + * @return {Boolean} + */ +methods.addMetric = (name, metric, overwrite = false) => { + if(name in metrics && !overwrite) + return false; + + metrics[name] = metric; + return true; +}; + +/** + * get the metrics register + * @author Ruben Meyer + * @return {Object} + */ +methods.getRegister = () => { + return register; +}; + +/** + * get the metrics client + * @author Ruben Meyer + * @return {Object} + */ +methods.getClient = () => { + return client; +}; + +module.exports = methods; diff --git a/bin/web/routes/api/prometheus.js b/bin/web/routes/api/prometheus.js new file mode 100644 index 0000000..b252f68 --- /dev/null +++ b/bin/web/routes/api/prometheus.js @@ -0,0 +1,44 @@ +let promClient = global['requireModule']('prometheus'); +let cfg = require(global['__dirname']+'/bin/config'); +let crypto = require('crypto'); + +module.exports = { + path: "/prometheus", + /** + * let prometheus query metrics + * @url /api/prometheus + * @method GET + */ + get: async (req, res) => { + // base64 encoded header + let b64auth = (req.headers.authorization || '').split(' ')[1] || ''; + let [user, password] = Buffer.from(b64auth, 'base64').toString().split(':'); + + // if request can be authenticated + if( + user + && password + && user.length == cfg.prometheus.auth_user.length + && password.length == cfg.prometheus.auth_pass.length + && crypto.timingSafeEqual( + Buffer.from(password, 'hex'), + Buffer.from(cfg.prometheus.auth_pass, 'hex') + ) + && crypto.timingSafeEqual( + Buffer.from(user, 'hex'), + Buffer.from(cfg.prometheus.auth_user, 'hex') + ) + ) { + res.set('Content-Type', promClient.getRegister().contentType); + return res.end(await promClient.getRegister().metrics()); + + // user is not logged in + } else { + res.set('WWW-Authenticate', 'Basic realm="401"') // change this + return res.type('json').end(JSON.stringify({ + status: 401, + message: 'msg.auth.login.required' + })); + } + } +}; diff --git a/package.json b/package.json index e78054c..d5daf73 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "express-session": "^1.17.1", "mongo-sanitize": "^1.1.0", "mongoose": "^5.9.28", + "prom-client": "^12.0.0", "pug": "^3.0.0" } }