1

I am developing a daemon started by upstart (Ubuntu 14.04) which needs to run as a non-privileged user (for security), but bind privileged port 443.

I am using setcap to set the CAP_NET_BIND_SERVICE capability for the executable (it's not a script). I am setting it in Permitted, Effective, and Inherited sets (setcap 'cap_net_bind_service+eip' EXEC).

I can su to the non-privileged user, and run it directly, and it works perfectly. It correctly binds the port, and /proc/PID/status shows the proper capabilities masks with 0x400 bit set.

But when I start the service via upstart it does not run with the capabilities specified for the binary, and the bind() fails (EPERM). /proc/PID/status shows capabilities masks are all 0.

Any ideas?

1

I'm now thinking this is a bug, and related to the way upstart starts services with "expect daemon" (i.e. services that fork twice upon startup). I notice that if I use strace on a process that is using capabilities(7) the capabilities are also ignored. I suspect that upstart, in order to determine the PID to wait on, traces a service specified with "expect daemon" long enough to obtain the PID, and that's causing the kernel capabilities mechanism to fail. So the bug is in the way that capabilities interact with process tracing, and the fact that upstart uses process tracing when starting a service with "expect daemon" (this is supposition).

As a simple test:

  1. Write a small C PROGRAM to bind to port 443 (you cannot use an interpreted language such as python with capabilities(7)).
  2. Run it as non-root, and see that it fails to bind due to lack of privilege.
  3. Set the CAP_NET_BIND_SERVICE capability for your PROGRAM (as root run setcap 'cap_net_bind_service+epi' PROGRAM)
  4. Run it as non-root, and see that it now succeeds.
  5. Now run it with strace, and see that it now fails.

(note that in step 3 strictly speaking the Inherited capability set (i flag) does not need to be modified for this test, but it does for a process that forks() such as my daemon).

I'll file a bug against the kernel about this, since nothing on the capabilities(7) man page says it should not work with process tracing.

1
  • If my bug report is accepted (validating my theory here) I will rewrite the original question and subject to reflect what I found, since Upstart is not the real culprit here but rather the interaction between ptrace and capabilities in the kernel. Upstart is going away anyway in favor of systemd, which appears to have integrated better with capabilities. – BobDoolittle Aug 5 '14 at 13:40
1

One workaround is to not use expect fork or expect daemon, and just make your daemon a foreground process. Then Upstart will not trace it at all.

1
  • Right I'm thinking of doing that, and taking advantage of start-stop-daemon instead to do the "daemonization". That has an advantage in that I can have it run the daemon as the UID I want, instead of using the Upstart 'setuid' command, which would allow me to have some pre-start and post-stop script code to do setup/cleanup that still runs as root (the Upstart setuid command is 'global' and applies to the entire service configuration). – BobDoolittle Aug 7 '14 at 16:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.