Lower worker privilege with cluster or fork when run as root and still able to use unprivilaged ports
var cluster = require('cluster'); | |
var util = require('util'); | |
var http = require('http'); | |
var numCPUs = 2; //require('os').cpus().length; | |
// | |
// general functions | |
// | |
function isRoot() { | |
return process.getuid() == 0; | |
} | |
// | |
// Check args | |
// | |
var runUser = process.argv[2], | |
runGroup = process.argv[3] || runUser; | |
if (isRoot() && !runUser) { | |
console.log('When runned as root a user needs to be given as argument!'); | |
process.exit(1); | |
} | |
// | |
// The real deal.... | |
// | |
if (cluster.isMaster) { | |
cluster.on('online', function(worker) { | |
console.log("Yay, the worker responded after it was forked: ", worker.process.pid); | |
}); | |
cluster.on('exit', function(worker, code, signal) { | |
console.log('worker ' + worker.process.pid + ' died'); | |
}); | |
cluster.on('listening', function(worker, listenData) { | |
console.log('worker ' + worker.process.pid + ' is listening on: ' + util.inspect(listenData)); | |
}); | |
// Fork workers. | |
for (var i = 0; i < numCPUs; i++) { | |
cluster.fork(); | |
} | |
} else { | |
// drop privileges to normal user if root | |
if (isRoot()) { | |
process.setgid(runUser); | |
process.setuid(runGroup); | |
} | |
// Workers can share any TCP connection | |
// In this case its a HTTP server | |
http.createServer(function(req, res) { | |
res.writeHead(200); | |
res.end("hello world from process:" + process.pid + "\n"); | |
}).listen(80); | |
} |
var fork = require('child_process').fork; | |
var util = require('util'); | |
var http = require('http'); | |
var net = require('net'); | |
var numCPUs = 2; //require('os').cpus().length; | |
// | |
// general functions | |
// | |
function isRoot() { | |
return process.getuid() == 0; | |
} | |
function isObject(o) { | |
return (typeof o === 'object' && o !== null); | |
} | |
function toDecInt(value) { | |
value = parseInt(value, 10); | |
return isNaN(value) ? null : value; | |
} | |
// | |
// Check args | |
// | |
var runUser = process.argv[2] || proces.getuid, | |
runGroup = process.argv[3] || runUser; | |
if (isRoot() && !runUser) { | |
console.log('When runned as root a user needs to be given as argument!'); | |
process.exit(1); | |
} | |
var isWorker = 'TEST_WORKER' in process.env, | |
isMaster = !isWorker; | |
var serverHandlers = {}; | |
function forkChild(customEnv) { | |
var child, | |
options = {}, | |
self = this, | |
env = process.env; | |
// Create env object | |
// first: copy and add id property | |
var envCopy = util._extend({}, env); | |
envCopy['TEST_WORKER'] = 'WORKER'; | |
// second: extend envCopy with the env argument | |
if (isObject(customEnv)) { | |
envCopy = util._extend(envCopy, customEnv); | |
} | |
// fork child | |
child = fork(process.argv[1], process.argv.slice(2), { | |
'env': envCopy, | |
'silent': false, | |
'execArgv': process.execArgv, | |
'uid': toDecInt(runUser), | |
'gid': toDecInt(runGroup) | |
}); | |
// proces child message | |
child.on('message', function (message) { | |
// This sequence of information is unique to the connection | |
// but not to the worker | |
var args = [message.address, | |
message.port, | |
message.addressType, | |
message.fd]; | |
var key = args.join(':'); | |
var handler; | |
if (serverHandlers.hasOwnProperty(key)) { | |
handler = serverHandlers[key]; | |
} else { | |
handler = serverHandlers[key] = net._createServerHandle.apply(net, args); | |
} | |
// echo callback with the fd handler associated with it | |
child.send({}, handler); | |
console.log('worker ' + message.pid + ' is listening on: ' + util.inspect(message)); | |
}); | |
child.on('error', function () { | |
console.error(arguments); | |
}); | |
return child; | |
} | |
// | |
// The real deal.... | |
// | |
if (isMaster) { | |
// Fork workers. | |
for (var i = 0; i < numCPUs; i++) { | |
forkChild(); | |
} | |
} else { | |
// Get a handle bound to the socket we want to use | |
var options = { | |
pid: process.pid, | |
address: '0.0.0.0', | |
port: 80, | |
addressType: 4, | |
fd: null | |
} | |
process.send(options); | |
process.on('message', function (message, handler) { | |
// Workers can share any TCP connection | |
// In this case its a HTTP server | |
http.createServer(function(req, res) { | |
console.log('Request on worker with pid: ' + process.pid); | |
res.writeHead(200); | |
res.end("hello world from process:" + process.pid + "\n"); | |
}).listen(handler); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment