Skip to content

Instantly share code, notes, and snippets.

@stolsma
Created August 25, 2012 13:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stolsma/3465889 to your computer and use it in GitHub Desktop.
Save stolsma/3465889 to your computer and use it in GitHub Desktop.
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