39 comments

[ 3.0 ms ] story [ 92.5 ms ] thread
It's probably a better idea to just run nginx on port 80 and proxy requests through in any sort of real environment. And if you're not in production...well, it doesn't really matter which port you choose anyway.
Very true. This will also let you run multiple instances of node and do load balancing.
Please excuse my ignorance, but why would I want to run nginx to proxy requests through?
[This response is a guess, and very generalized]

You probably won't handle every HTTP edge case as well as nginx defaults, and you don't really need the control at that level to implement a functional, high performance web app.

Possibly because Nginx serves static content much faster than Node.
The reasons that other folks have mentioned (static files, nginx is built for lots of HTTP requests), as well as basic load balancing between several 'worker processes'. Sometimes you'll want to have several app server processes running with several ports open, and have nginx load balance between all of those processes (then load balance again at a higher tier between all of the different nginx processes.)
If you're running a few services and sites on the same box you need some sort of proxy listening on port 80 reverse proxying incoming requests to the correct internal port. i.e. an incoming request looking for "www.mynodeapi.com" could be internally routed to "127.0.0.1:8080" (or wherever your node app is listening). The proxy could be nginx, haproxy or perhaps even a node app using node-http-proxy. AFAIK, if you need proper web sockets support your proxy has to be node based or haproxy.
Alternatively, you could grant the node binary the ability bind ports < 1024, using setcap (Linux only):

    sudo setcap 'cap_net_bind_service=+ep' /path/to/nodejs
Then you don't have to ever run it as root (at least not for the purpose of binding to the right port).
Endless bonus points to you for mentioning capabilities. They're so unknown but gems to come across.
Or route port 80 to your node port (eg. 8080):

  sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
Friendly reminder: iptables has a significant performance penalty on high traffic servers.

If your service gets a few thousand hits per day, it'll be okay. If you're trying to survive a few thousand hits per second, your clients will suffer less than optimal throughput.

Friendly reminder @ seiji: don't make those kind of statements without backing these, please ?

In what use case would iptables give a performance penalty ?

I have iptable-setups running with hundreds of rules and 800-1000 concurrent(!) users. During traffic peaks times my 5-6(?) year old Xeon does a good job, cpu usage barely touches 3-4%. Throughput downstream at that point around 350Mbit/s (admitted, downstream just matched by conntrack:matched/established) and upstream around 20Mbit/s, working through hundreds of rules.

iptables runs in the kernel-space and is very, very, VERY performant. (well, I lied, iptables itself is just a configuration tool for the kernel - but it is incredibly fast).

Following that kind of traffic with tcpdump (userland) just drops about half of the packets because it maxes out that poor CPU instantly. (yes, that depends on the args). And don't even thinkg about using iptraf :-)

(That reminds me: please, some one, send me better server hardware :-x)

He could have meant iptables state tracking, which can often overflow in the face of high traffic/connection rates.

1. Don't use connection tracking

2. If you need to use it, make judicious use of -j NOTRACK

I use this on a few high traffic servers, with good results:

  iptables -t raw -A PREROUTING -i lo -j NOTRACK
  iptables -t raw -A OUTPUT -o lo -j NOTRACK

  iptables -A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
  iptables -A INPUT -i eth0 -p tcp -m tcp --dport 443 -j ACCEPT
  
  iptables -t raw -A OUTPUT -o eth0 -p tcp -m tcp --sport 443 -j NOTRACK
  iptables -t raw -A OUTPUT -o eth0 -p tcp -m tcp --sport 80 -j NOTRACK
Well crap. Replying to myself. Missed a pair of rules in my earlier copy/paste:

  iptables -t raw -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j NOTRACK
  iptables -t raw -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j NOTRACK
That kinda defeats the purpose of restricting ports under 1024.
This only grants that right to a single executable--it doesn't allow everyone on the system to bind to low port numbers.
It kinda does, unless you restrict execution of nodejs to root/nodejs group anyways. It's like setting /bin/bash setuid.
That's true--since node can run arbitrary code for you, you'd want to restrict execution of node to a known set of users.
I don't really understand. Does it grant nodejs root privileges? I assumed it ONLY allowed for binding to port 80, not all root privileges...
What was the purpose of that arbitrary limitation again? It's mostly a holdover from the olden days we now have to constantly work around (by mostly having lazy people run things as root everywhere).
setcap solves the problem in the wrong way in this scenario imho: With setcap, any user could run node with a node script using ports < 1024.

What would be more useful is the ability to allow a _user_ to open a privileged port. In my option mappu's answer is the right way to go, i.e. using authbind to allow a certain user to open a port or a range of ports.

Just make sure node is only executable by a restricted group of users.

This limits it both on the basis of which user can open ports and which programs can.

Authbind looks really appropriate here.

(Hmm, did parent just edit his comment ? He didn't mention authbind when I hit reply, did he ?)

From the man page: authbind allows a program which does not or should not run as root to bind to low-numbered ports in a controlled way. The shared library loaded using LD_PRELOAD overrides the bind(2) system call. When a program invoked via authbind calls bind to bind a socket to a low-numbered TCP/IP port, and if the program doesn't already have an effective uid of 0, the version of bind supposed by authbind forks and executes a setuid-root helper program.

You can create configuration file like /etc/authbind/byport/port and use standard linux file permissions to allow certain non-root users to bind to ports < 1024

If you are on Solaris (10 or higher works from experience) you can grant a single user the ability to bind to the lower ports:

  usermod -K defaultpriv=basic,net_privaddr ${user}
You can off course also assign the privileges to a role, and then assign the user that role so as required they can su to the role and use the privileges.

Here is a pretty neat description of what is possible and why it is pretty awesome: http://www.c0t0d0s0.org/archives/4075-Less-known-Solaris-fea...

---

On FreeBSD if you have the MAC framework enabled, you can use the portacl module to give new privileges:

  sysctl security.mac.portacl.rules=uid:$user_id:tcp:80
See http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/ma... for more information.
Not a good idea to hardcode the name of the "owner". Standard practice is to setuid/gid to 65535 (nobody).
Actually, even in the classical Unix security model, setuid'ing to nobody is considered a bad idea. Because so many services do it, giving an attacker an opportunity to become nobody will likely grant him access to those other services as well.
Point taken. Allow me to revise my statement.

"In production, one should create a specialized user to run any service." And if you have lots of money to waste, stick 'em all in separate VMs too.

Looking at the snippet of code: Isn't that sample (probably) confusing group and user?

  process.setgid('tlhunter');
  process.setuid('users');
I'd expect that 'users' is the group here and 'tlhunter' the user 'thomas l. hunter'?
Yep; A typo I made when converting variables to strings for the example.
You could also create a unix socket and have node.js bind on it.
Alternatively, if you don't trust a large program with dropping root, you can factor out the binding and listening into a separate program. Then accepting and everything beyond can be done with normal privileges.

Assuming a tcpserver-like program called tcplisten, this would look like

    sudo tcplisten 0.0.0.0 80 setuidgid nobody \
      program-that-accepts-on-stdin
FastCGI works similarly. Multiple workers can run underneath, calling accept(2) on stdin.

A simple implementation of tcplisten:

https://gist.github.com/4211098

I wrote a similar article a few weeks ago (http://syskall.com/dont-run-node-dot-js-as-root/) but have since then realized that changing the process' UID brings a lot of unexpected problems and a much simpler solution is to use a higher port and proxy via nginx. For example, if you initialize a logger before starting your HTTP server and changing the process UID, you will might create root owned files and eventually run into permission conflicts.