Bind NodeJS to port 80 - solution

492 views
Skip to first unread message

Matthew

unread,
Jul 16, 2010, 2:36:53 AM7/16/10
to nodejs
Here's a module I wrote that will let you switch the user and group of
a NodeJS process: http://github.com/mattcg/swug

Run example-port-80.js as root - the HTTP server will bind to port 80
and then set its user and group to nobody, de-privileging itself.

It's got lots of debugging built in.

Isaac Schlueter

unread,
Jul 16, 2010, 5:43:57 PM7/16/10
to nod...@googlegroups.com
Oh no!

> exec('perl -e \'($login,$pass,$uid,$gid) = getpwuid(' + olduid + ');print "$login";\''

What was the status of getting the passwd data? Wasn't that discussed
once upon a time? I know we didn't want it to be a blocking API,
since it needs to hit the file system, but shelling out to perl is so
not good.

--i

> --
> You received this message because you are subscribed to the Google Groups "nodejs" group.
> To post to this group, send email to nod...@googlegroups.com.
> To unsubscribe from this group, send email to nodejs+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/nodejs?hl=en.
>
>

Marak Squires

unread,
Jul 16, 2010, 5:49:59 PM7/16/10
to nod...@googlegroups.com
I must be missing something here. 

What's the most common use case for needing to do something like this?

Why not just start the application under the correct user and group? What are the benefits for being able to swap?

Timothy Caswell

unread,
Jul 16, 2010, 5:52:37 PM7/16/10
to nod...@googlegroups.com
Quite simple really, you have to be root to bind to ports lower than 1024 (most production sites do this)

But you don't want your app running as root after then because if there is a security hole, then root is exposed.

Stephen Belanger

unread,
Jul 16, 2010, 6:00:22 PM7/16/10
to nod...@googlegroups.com
I would think having a user switching system integrated into your app would be just as insecure, if not moreso, than running it with root permissions.

Timothy Caswell

unread,
Jul 16, 2010, 6:03:38 PM7/16/10
to nod...@googlegroups.com
I just finally figured out my own solution to this problem for sites I host.  I use upstart + spark/connect


Technically running as root and then downgrading isn't needed for port 5050, but it would work the same for port 80

notice that the user in htop is me not root.

Kenneth Grome

unread,
Jul 16, 2010, 6:04:53 PM7/16/10
to nod...@googlegroups.com
> I would think having a user switching system integrated
> into your app would be just as insecure, if not moreso,
> than running it with root permissions.

Can't you just delete this module after you're done using it?

Sincerely,
Kenneth Grome

Isaac Schlueter

unread,
Jul 16, 2010, 6:12:21 PM7/16/10
to nod...@googlegroups.com
On Fri, Jul 16, 2010 at 15:03, Timothy Caswell <t...@creationix.com> wrote:
> I just finally figured out my own solution to this problem for sites I host.
>  I use upstart + spark/connect

Heheh...

"I figured out my own solution: use someone else's solution!" ;P

Seriously, that's exactly the right way to do this. It'd be a cool
and interesting project to write upstart or Monit in node. That's
kind of what node-supervisor is the start of, albeit with hardly any
features at this point.

But unless this is ALL your app does, and it does it in a really
generalizable way, it shouldn't do it at all, in my opinion.

--i

Brett Morgan

unread,
Jul 16, 2010, 5:53:07 PM7/16/10
to nod...@googlegroups.com
To be able to bind to port 80 your process must be root, but you probably don't want to trust the code written by the interns in the office with being root on your main webserver...

that said, i prefer the option of having nginx running as the port 80 bound process, and node.js running as a non priv'd user on a high port behind it.

principle of least amouunt of privileged code and all that

Brett Morgan

unread,
Jul 16, 2010, 6:06:24 PM7/16/10
to nod...@googlegroups.com
generally once you've shed privs you can't get 'em back...


--
You received this message because you are subscribed to the Google Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com.
To unsubscribe from this group, send email to nodejs+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nodejs?hl=en.




--
Brett Morgan
http://www.google.com/profiles/brett.morgan

Stephen Belanger

unread,
Jul 16, 2010, 6:20:30 PM7/16/10
to nod...@googlegroups.com
I agree with Brett. I just run node.js on 8080 and proxy everything through ngix while using a "static" subdomain for static files.

Timothy Caswell

unread,
Jul 16, 2010, 6:21:14 PM7/16/10
to nod...@googlegroups.com
It's one thing if you don't trust the people writing your code and you can afford the performance hit of a reverse proxy like nginx.

But most my use cases, I trust the people writing the code (me) and it's the people on the internet that I want to be protected from.  Then it makes perfect sense to bind to port 80 directly and then drop uid.

Marak Squires

unread,
Jul 16, 2010, 6:21:46 PM7/16/10
to nod...@googlegroups.com
I wasn't aware you couldn't bind to ports below 1024 if you weren't root, good to know. 

We spawn multiple node instances up as various users on various ports, but everything aside from the front-facing load balancer is running on ports greater then 8000, so I guess it never came up.

The load balancer itself is running as root though.....that's not exactly ideal.....

Does something need to be patched in node to make this work? I could have sworn I've bound to port 80 for non-root users in Apache.

Jacob Rothstein

unread,
Jul 16, 2010, 6:23:02 PM7/16/10
to nod...@googlegroups.com

Stephen Belanger

unread,
Jul 16, 2010, 6:26:21 PM7/16/10
to nod...@googlegroups.com
If you want to bind directly to 80 that's your choice, but personally I think it's a trade-off either way--the reverse proxy will slow regular node connections a bit, but static file requests are faster on nginx, so both sides have their advantages. I suppose it's a matter of personal preference and the performance needs of your app.

Matthew

unread,
Jul 16, 2010, 6:50:03 PM7/16/10
to nodejs
> Does something need to be patched in node to make this work? I could have
> sworn I've bound to port 80 for non-root users in Apache.

No, nothing needs to be patched. Apache uses a mechanism similar to
what I've implemented here - it binds to port 80 as root then switches
user.

Marak Squires

unread,
Jul 16, 2010, 6:51:22 PM7/16/10
to nod...@googlegroups.com
Can this be achieved without using perl?


--

Matthew

unread,
Jul 16, 2010, 6:58:04 PM7/16/10
to nodejs
> exec('perl -e \'($login,$pass,$uid,$gid) = getpwuid(' + olduid + ');print "$login";\''

> ...shelling out to perl is so
> not good.

You're right, but I'd relax. I don't think anyone would want to switch
user more than a few times in an app. Either way, I'll probably find a
better alternative to that line soon.

Timothy Caswell

unread,
Jul 16, 2010, 6:58:17 PM7/16/10
to nod...@googlegroups.com

Matthew

unread,
Jul 16, 2010, 7:02:02 PM7/16/10
to nodejs
> Can this be achieved without using perl?

I haven't looked for a better alternative much yet. If you find one,
please let me know.

Python has pwd and grp, interfaces to the user and group databases.
The ideal would be something like that.

Isaac Schlueter

unread,
Jul 16, 2010, 7:05:10 PM7/16/10
to nod...@googlegroups.com

That's what I was asking about. A while back, I seem to recall that
we'd seen patches to bind to getgroupname, setgroupname, etc., but
since those are blocking APIs, they need to be clevered up a bit.

--i

Timothy Caswell

unread,
Jul 16, 2010, 7:07:55 PM7/16/10
to nod...@googlegroups.com
yeah, it's built in!

process.setuid("tim");

that's all it takes.

Matthew

unread,
Jul 16, 2010, 7:22:47 PM7/16/10
to nodejs
Yay! Thank you.

Terry Riegel

unread,
Jul 16, 2010, 7:45:14 PM7/16/10
to nod...@googlegroups.com
Is there a process.setgid('node'); ?

Sent from my iPhone

Matthew

unread,
Jul 16, 2010, 7:58:26 PM7/16/10
to nodejs
> Is there a process.setgid('node'); ?

Yes. You will typically need to use setgid before setuid if you intend
to do both.

Matthew

unread,
Jul 16, 2010, 8:29:05 PM7/16/10
to nodejs
> yeah, it's built in!
>
> process.setuid("tim");
>
> that's all it takes.

Just remembered - originally, all those exec's were needed because I
was certain setuid only accepted a numerical ID. In fact, the docs at
nodejs.org say:

> process.getuid(), process.setuid(id)
> Gets/sets the user identity of the process. (See setuid(2).) This is the numerical userid, not the username.

Aria Stewart

unread,
Jul 20, 2010, 10:38:08 AM7/20/10
to nod...@googlegroups.com

On Jul 16, 2010, at 6:20 PM, Stephen Belanger wrote:

> I agree with Brett. I just run node.js on 8080 and proxy everything through
> ngix while using a "static" subdomain for static files.

And suffer NGINX's HTTP-1.0 proxies, the lack of seeing the real socket to the client.

Aria Stewart

unread,
Jul 20, 2010, 10:38:06 AM7/20/10
to nod...@googlegroups.com

On Jul 16, 2010, at 6:00 PM, Stephen Belanger wrote:

> I would think having a user switching system integrated into your app would
> be just as insecure, if not moreso, than running it with root permissions.


Hardly: It's a well-understood system interface -- and you can only shed privilege, not gain it again.

Aria Stewart

unread,
Jul 20, 2010, 10:38:39 AM7/20/10
to nod...@googlegroups.com

On Jul 16, 2010, at 6:21 PM, Marak Squires wrote:
>
> Does something need to be patched in node to make this work? I could have
> sworn I've bound to port 80 for non-root users in Apache.


It starts as root, then drops privilege.

Troy

unread,
Jul 20, 2010, 1:24:40 PM7/20/10
to nodejs
The docs http://nodejs.org/api.html#process-setuid-68 say it has to be
the numerical id (also not sure how long the link to the docs will
work, as it appears the "68" in the url is a sequence #, which will
probably change if a new api entry is added to the docs above this
one)

--troy

Peter Griess

unread,
Jul 20, 2010, 2:09:44 PM7/20/10
to nod...@googlegroups.com
Specifying a username to setuid() or groupname to setgid() has been supported for a few releases -- the docs were just never updated. The docs were fixed recently with a9d8cac4b0 and will be reflected next time there's a release.

Peter
Reply all
Reply to author
Forward
0 new messages