1

According to Apple's launchd documentation:

[launchd] Eliminates the primary reason for running daemons as root. Because launchd runs as root, it can create low-numbered TCP/IP listen sockets and hand them off to the daemon.

That's good ... but how?

I want to do the following:

  • start the Nginx webserver at boot time
  • have it listen to port 80
  • have it run as the _www user

which is exactly the scenario the above documentation is talking about. But I haven't found a way to do it that doesn't give me a bind() to 0.0.0.0:80 failed (13: Permission denied) error message.

Here's the .plist file I have at /Systems/Library/LaunchDemons/homebrew.mxcl.nginx.plist:

<!--
Adapted from the .plist file provided as part of
the Mac Homebrew distribution of nginx.
-->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>


<!-- 
Here's the problem!!

    <key>Username</key>
    <string>_www</string>

-->

<!--
I thought that Sock(et) declarations would be
the way to tell launchd to hand off a port to the
daemon. So I added these lines, but they don't
appear to make any difference at all.
-->

    <key>SockServiceName</key>
    <string>http</string>

    <key>SockType</key>
    <string>stream</string>

    <key>SockFamily</key>
    <string>IP4</string>

    <key>Label</key>
    <string>homebrew.mxcl.nginx</string>

    <key>RunAtLoad</key>
    <true/>

    <key>KeepAlive</key>
    <false/>

    <key>StandardOutputPath</key>
    <string>/var/log/nginx.log</string>

    <key>StandardErrorPath</key>
    <string>/var/log/nginx.log</string>

    <key>ProgramArguments</key>
    <array>
      <string>/usr/local/opt/nginx/bin/nginx</string>
    </array>

    <key>WorkingDirectory</key>
    <string>/usr/local</string>

  </dict>
</plist>

As you can see, the Username: _www is commented out. So when I run launchd as root, nginx runs as root, and everything works fine. But when I include the Username: _www declaration, nginx fails and leaves this error message in the logfile:

nginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied)

What am I missing?

9
  • 1
    I believe that won't work, even with launchd. AFAICT you need to run it as root. You can make the nginx itself drop the privileges afterwards. I'm not quite sure about what needs to be changed in nginx.conf, but there should be something like User - change it to _www.
    – jweyrich
    Mar 10, 2014 at 5:40
  • 1
    @jweyrich Unless I'm mistaken, the Apple docs clearly state that what I'm asking for is possible. See the beginning of the question. Thanks for point out nginx's 'user' directive. If I can't find a better way, I'll use that. However, nginx only applies this directive to the worker processes -- the master process still runs as root. (I've just tested it to be sure.)
    – Pitarou
    Mar 10, 2014 at 6:07
  • 1
    You're absolutely right when you say it's possible, however, the launchd does not do any magic. A daemon needs to "get" the socket bound by launchd (e.g.: launch_data_get_fd) - it needs to be launchd-aware and implement some logic internally. AFAIK that's not the case for nginx.
    – jweyrich
    Mar 10, 2014 at 6:12
  • 1
    @jweyrich I see. Thanks for taking the time to make that clear. :-)
    – Pitarou
    Mar 10, 2014 at 6:13
  • 1
    No problem. You're welcome.
    – jweyrich
    Mar 10, 2014 at 6:15

1 Answer 1

2

A daemon needs to "get" the socket bound by launchd (e.g.: launch_data_get_fd) - it needs to be launchd-aware and implement some logic internally. AFAIK that's not the case for nginx, so I suggest one of the following options:

  • Adjust the nginx port to be > 1024, then setup your firewall to redirect traffic from/to port 80 to/from the nginx port;
  • Let the launchd run nginx as root and make nginx drop its privileges afterwards by changing the user directive on its configuration file;
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .