A common use for Node.js is to build web applications. Usually, we want these applications to listen on port 80. As a security precaution, most OS’s require root privileges for this to happen (e.g. OS X, Linux, BSD). To run a Node application this way, we need to do the following:
sudo node server.js
Then, the application runs as root for the rest of the session. There are potential security risks to this, though. What if there is a vulnerability in your application and a hacker starts controlling your app and doing naughty things with it? Thankfully, we can drop the user account running our process to a less secured user, such as our normal account. There are two methods on the process global which can handle this for us, .setgid() and .setuid().
Here’s a working example of this in action. We want to run this code AFTER we bind to port 80, so we run the code in a callback:
app.listen(80, 'localhost', null, function() {
// Listening
try {
console.log('Old User ID: ' + process.getuid() + ', Old Group ID: ' + process.getgid());
process.setgid('users');
process.setuid('tlhunter');
console.log('New User ID: ' + process.getuid() + ', New Group ID: ' + process.getgid());
} catch (err) {
console.log('Cowardly refusing to keep the process alive as root.');
process.exit(1);
}
});
The results of .getuid() and .getgid() will be numeric, and aren’t really useful, other than showing that something happened. They’ll probably both say 0 the first time (that’s root in *nix land), and then some bigger number afterwards.
Unsure of your username or group name? For shame! Run the following commands:
whoami groups
I think you got your username and group names swapped in the code snippet.
Nice catch. I swapped out some variables with strings and messed it up.
This is not a good way to do it IMO.
Instead consider authbind.
Installation is simple: add a file to /etc/authbind/byport/80 that is executable by the user you want to run the app (let’s say ‘www-data’).
Then run authbind as www-data. Here’s an excerpt from an upstart script I use:
This simple command runs authbind as the user www-data. Because /etc/authbind/byport/80 is executable by www-data, it can run on port 80 with no root privileges whatsoever.
Nice, wasn’t aware of authbind. Looks like it isn’t available on OS X natively or via homebre.
Guessing your uid isn’t “users”, and your gid isn’t “tlhunter” – I think you have the arguments to setgid and setuid swapped.
Why not just use authbind?
Bad: Do this
Better: Use auto bind
Best: Use iptables, ipfw, or a reverse proxy through nginx, Apache, or Varnish to move traffic from port 80 to your node app running on a high port (>2000ish)
s/auto\ bind/authbind
I blame OS X’s autocorrect.
You could also use iptables to redirect port 80 to a port that doesn’t require root privileges.
May I suggest using nginx or varnish in front of the node.js server. That, or simply using port forwarding to another port.
There is a great tool I’m using for my linux box, running multiple node servers. I found it in the great djbdns server.
It’s called daemontools and it supports auto-restart, logging STDOUT & STDERR, running from different accounts and a lot more.
I’m using apache and virtualhosts for redirection to my node apps ( they use ports 800+ ).
you could always spawn it in .sh and use the battle-tested ‘daemon -u’
or socat.
sudo socat -4 TCP-LISTEN:80,fork TCP:127.0.0.1:9669on opensolaris or solaris 10 systems you may also grant the ‘net_privaddr’ privilege to a user or role.
If you’re deploying to a Ubuntu box, you can also use Upstart to manage your Node.js instance, and setting the process runner to a unprivileged user.
Something like
exec sudo -u deploy sh -c "cd /your/app && node server.js".