diff --git a/nodejs/authentication/index.js b/nodejs/authentication/index.js new file mode 100644 index 0000000..e172023 --- /dev/null +++ b/nodejs/authentication/index.js @@ -0,0 +1,71 @@ +module.exports = function(config) { + + const crypto = require('crypto'); + const password = crypto.randomBytes(256); + var cookie = require('cookie-encryption'); + // const cipher = crypto.createCipher('aes256', password); + // const decipher = crypto.createDecipher('aes256', password); + // var encrypted = cipher.update(JSON.stringify(user), 'utf8', 'base64') + // + cipher.final('base64'); + // console.log("encrypted", encrypted); + // var decrypted = decipher.update(encrypted, 'base64', 'utf8') + decipher.final('utf8'); + // console.log("decrypted", decrypted); + + var authentication = function (req, res, next) { + return next(); + } + + if (config) { + + var cipher = config.cookies && config.cookies.cipher ? config.cookies.cipher : "aes256"; + + authentication = function (req, res, next) { + + function unauthorized(res) { + res.setHeader('WWW-Authenticate', 'Basic realm=Authorization Required'); + res.status(401).send('Not logged in. Login'); + }; + + var user = require('basic-auth')(req); + var vault = cookie('credentials'); + + if (!user || !user.name || !user.pass) { + return unauthorized(res); + }; + + if (config.passwords && config.passwords[user.name]) { + if (crypto.getHashes().indexOf(config.passwords[user.name][0])>=0) { + if (crypto.createHash(config.passwords[user.name][0]) + .update(user.pass, 'utf8').digest('hex') === config.passwords[user.name][1]) { + return next(); + } + } else { + console.log("**** HASH NOT FOUND ****"); + console.log(config.passwords[user.name][0]); + console.log(crypto.getHashes()); + } + } + if (config.ldap) try { + var LdapAuth = require('ldapauth'); + var auth = new LdapAuth(config.ldap); + auth.authenticate(user.name, user.pass, function(err, usr) { + auth.close(function(err) {}) + if (err) { + console.log("**** ERROR: LDAP Authentication failed:", err); + return unauthorized(res); + } + console.log("**** SUCCESS: LDAP Authentication:"); + return next(); + }); + return; // need to block here! + } catch (e) { + console.log("**** Error: LDAP failed: ", e, e.stack); + } + return unauthorized(res); + }; + + } + + return authentication; + +} diff --git a/nodejs/etc/servicedock.json b/nodejs/etc/servicedock.json index 078d6c0..c9ac713 100644 --- a/nodejs/etc/servicedock.json +++ b/nodejs/etc/servicedock.json @@ -1,3 +1,23 @@ { - "port": 8888 + "port": 8888, + "restrict": { + "cookies": { + "cipher": "aes256" + }, + "passwords": { + "marc": ["sha256", "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"], + "foo": ["sha256", "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9"] + }, + "ldap": { + "tlsOptions": { + "requestCert": false, + "rejectUnauthorized": false + }, + "url": "ldap://dev.marc.waeckerlin.org", + "adminDn": "cn=tmp,ou=system,ou=people,dc=dev,dc=marc,dc=waeckerlin,dc=org", + "adminPassword": "dGg7benUnZ9z", + "searchBase": "ou=person,ou=people,dc=dev,dc=marc,dc=waeckerlin,dc=org", + "searchFilter": "(uid={{username}})" + } + } } diff --git a/nodejs/package.json.in b/nodejs/package.json.in index 626e51e..7419d3b 100644 --- a/nodejs/package.json.in +++ b/nodejs/package.json.in @@ -1,30 +1,33 @@ { - "name": "@PACKAGE_NAME@", - "version": "@PACKAGE_VERSION@", - "private": true, - "dependencies": { - "express": "~2.5.8", - "stylus": "~0.53.0", - "ejs": ">= 0.0.1", - "socket.io": "~1.4.4", - "pty.js": "~0.3.0", - "async": "~1.5.2" - }, - "description": "Docker as a Service", - "main": "servicedock.js", - "devDependencies": {}, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "Marc Wäckerlin", - "license": "LGPL3", - "path": { - "prefix": "@PREFIX@", - "sysconf": "@SYSCONFDIR@", - "pkgdata": "@PKGDATADIR@", - "localstate": "@LOCALSTATEDIR@", - "log": "@LOCALSTATEDIR@/log/@PACKAGE_NAME@.log", - "config": "@SYSCONFDIR@/@PACKAGE_NAME@.json", - "nodejs": "@PKGDATADIR@/nodejs" - } + "name": "@PACKAGE_NAME@", + "version": "@PACKAGE_VERSION@", + "private": true, + "description": "Docker as a Service", + "main": "servicedock.js", + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Marc Wäckerlin", + "license": "LGPL3", + "path": { + "prefix": "@PREFIX@", + "sysconf": "@SYSCONFDIR@", + "pkgdata": "@PKGDATADIR@", + "localstate": "@LOCALSTATEDIR@", + "log": "@LOCALSTATEDIR@/log/@PACKAGE_NAME@.log", + "config": "@SYSCONFDIR@/@PACKAGE_NAME@.json", + "nodejs": "@PKGDATADIR@/nodejs" + }, + "dependencies": { + "express": "~2.5.8", + "stylus": "~0.53.0", + "ejs": ">= 0.0.1", + "socket.io": "~1.4.4", + "pty.js": "~0.3.0", + "async": "~1.5.2", + "basic-auth": "~1.0.3", + "ldapauth": "~2.2.4", + "cookie-encryption": "~1.4.2" + } } diff --git a/nodejs/public/javascripts/servicedock.js b/nodejs/public/javascripts/servicedock.js index 85d8299..f6ff27c 100644 --- a/nodejs/public/javascripts/servicedock.js +++ b/nodejs/public/javascripts/servicedock.js @@ -8,7 +8,7 @@ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 -var socket = io.connect(); +var socket = null; var focused = null; var docker = new Docker(); @@ -597,6 +597,12 @@ function initForms() { } function init() { + $("#logout").attr("href", + window.location.protocol+"//X:X@" + +window.location.hostname + +(window.location.port?":":"")+window.location.port + +window.location.pathname); + socket = io.connect(); socket.io .on("connect", connected) .on("reconnect", connected) diff --git a/nodejs/routes/index.js b/nodejs/routes/index.js index 5484ae0..fc621e9 100644 --- a/nodejs/routes/index.js +++ b/nodejs/routes/index.js @@ -1,4 +1,3 @@ - /* * GET home page. */ @@ -6,8 +5,8 @@ var package = require(__dirname+"/../package.json"); exports.index = function(req, res) { - res.render('index', { - packagename: package.name, - packageversion: package.version + res.render('index', { + packagename: package.name, + packageversion: package.version }); }; diff --git a/nodejs/servicedock.js b/nodejs/servicedock.js index c7953d8..f8e6034 100644 --- a/nodejs/servicedock.js +++ b/nodejs/servicedock.js @@ -1,55 +1,69 @@ +try { -/** - * Module dependencies. - */ - -var express = require('express') - , routes = require(__dirname+'/routes'); - -var app = module.exports = express.createServer(); -var io = require('socket.io').listen(app); -var sockets = require(__dirname+'/sockets')(io); -var package = require(__dirname+'/package.json'); -var config = require(package.path.config); -var docker = require(__dirname+'/docker')(app); - -// Configuration -process.argv.forEach(function(val, index) { - if (index<2) {return} - if (index!=2 || typeof val != 'number') { - console.log("**** ERROR: Unexpected Argument - allowed is only a port number"); + process.on('uncaughtException', function(e) { + console.log("**** UNCAUGHT EXCEPTION ****"); + console.log(e); + console.log(e.stack); process.exit(1); - } - config.port = val; -}); -if (typeof config.port != 'number') { - console.log("**** WARNING: no valid port given, defaults to 8888"); - config.port = 8888; -} + }); + + /** + * Module dependencies. + */ + + var express = require('express') + , routes = require(__dirname+'/routes'); -app.configure(function(){ - app.set('views', __dirname + '/views'); - app.set('view engine', 'ejs'); - app.use(express.bodyParser()); - app.use(express.methodOverride()); - app.use(require('stylus').middleware({ src: __dirname + '/public' })); - app.use(app.router); - app.use(express.static(__dirname + '/public')); -}); + var app = module.exports = express.createServer(); + var io = require('socket.io').listen(app); + var sockets = require(__dirname+'/sockets')(io); + var package = require(__dirname+'/package.json'); + var config = require(package.path.config); + var docker = require(__dirname+'/docker')(app); + var authentication = require(__dirname+'/authentication')(config.restrict); -app.configure('development', function(){ - app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); -}); + // Configuration + process.argv.forEach(function(val, index) { + if (index<2) {return} + if (index!=2 || typeof val != 'number') { + console.log("**** ERROR: Unexpected Argument - allowed is only a port number"); + process.exit(1); + } + config.port = val; + }); + if (typeof config.port != 'number') { + console.log("**** WARNING: no valid port given, defaults to 8888"); + config.port = 8888; + } + + app.configure(function(){ + app.set('views', __dirname + '/views'); + app.set('view engine', 'ejs'); + app.use(express.bodyParser()); + app.use(express.methodOverride()); + app.use(require('stylus').middleware({ src: __dirname + '/public' })); + app.use(app.router); + app.use(express.static(__dirname + '/public')); + }); -app.configure('production', function(){ - app.use(express.errorHandler()); -}); + app.configure('development', function(){ + app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); + }); -// Routes + app.configure('production', function(){ + app.use(express.errorHandler()); + }); -app.get('/', routes.index); + // Routes + app.get('/', authentication, routes.index); -app.listen(config.port, function() { - console.log("Express server listening on port %d in %s mode", - app.address().port, app.settings.env); -}); + app.listen(config.port, function() { + console.log("Express server listening on port %d in %s mode", + app.address().port, app.settings.env); + }); +} catch (e) { + console.log("**** EXCEPTION ****"); + console.log(e); + console.log(e.stack); + process.exit(1); +} diff --git a/nodejs/sockets/index.js b/nodejs/sockets/index.js index eda8a5f..774d7ce 100644 --- a/nodejs/sockets/index.js +++ b/nodejs/sockets/index.js @@ -13,8 +13,12 @@ module.exports = function(io) { } function exec(cmd, callback) { - console.log("== "+cmd); - proc.exec(cmd, callback); + if (cmd.length>40) { + console.log("== "+cmd.slice(0, 30+cmd.slice(30).indexOf(' '))+" ..."); + } else { + console.log("== "+cmd); + } + proc.exec(cmd, {maxBuffer: 10*1024*1024}, callback); } function fail(txt, data) { @@ -68,7 +72,8 @@ module.exports = function(io) { return fail("list docker images failed", { error: error, stderr: stderr, stdout: stdout }); - exec("docker inspect "+stdout.trim().replace(/\n/g, " "), imageinspect); + exec("docker inspect "+stdout.trim().replace(/\n/g, " "), + imageinspect); } function updateimages(error, stdout, stderr) { @@ -80,8 +85,8 @@ module.exports = function(io) { } function connection(socket) { - - console.log("new client"); + + console.log("new connection"); function emit(signal, data, info) { if (typeof data == 'string' && !data.match("\n")) { diff --git a/nodejs/views/index.ejs b/nodejs/views/index.ejs index 3e205fb..0b61b0c 100644 --- a/nodejs/views/index.ejs +++ b/nodejs/views/index.ejs @@ -31,6 +31,7 @@