Monday, June 4, 2012

Is there a way for non-root processes to bind to "privileged' ports (<1024) on Linux?


It's very annoying to have this limitation on my development box, when there won't ever be any users other than me.



I'm aware of the standard workarounds , but none of them do exactly what I want:




  1. authbind (The version in Debian testing, 1.0, only supports IPv4)


  2. Using the iptables REDIRECT target to redirect a low port to a high port (the "nat" table is not yet implemented for ip6tables, the IPv6 version of iptables)

  3. sudo (Running as root is what I'm trying to avoid)

  4. SELinux (or similar). (This is just my dev box, I don't want to introduce a lot of extra complexity.)



So is there some simple sysctl variable for this, or am I just out of luck?



EDIT: In some cases, you can use capabilities to do this.


Source: Tips4all

10 comments:

  1. Okay, thanks to the people who pointed out the capabilities system and CAP_NET_BIND_SERVICE capability. If you have a recent kernel, it is indeed possible to use this to start a service as non-root but bind low ports. The short answer is that you do:

    setcap 'cap_net_bind_service=+ep' /path/to/program


    And then anytime program is executed thereafter it will have the CAP_NET_BIND_SERVICE capability. setcap is in the debian package libcap2-bin.

    Now for the caveats:


    You will need at least a 2.6.24 kernel
    This won't work if your file is a script. (ie, uses a #! line to launch an interpreter). In this case, as far I as understand, you'd have to apply the capability to the interpreter executable itself, which of course is a security nightmare, since any program using that interpreter will have the capability. I wasn't able to find any clean, easy way to work around this problem.


    Resources:


    capabilities(7) man page. Read this long and hard if you're going to use capabilities in a production environment. There are some really tricky details of how capabilities are inherited across exec() calls that are detailed here.
    "Bind ports below 1024 without root on GNU/Linux": The document that first pointed me towards setcap.

    ReplyDelete
  2. The standard way is to make them "setuid" so that they start up as root, and then they throw away that root privilege as soon as they've bound to the port but before they start accepting connections to it. You can see good examples of that in the source code for Apache and INN. I'm told that Lighttpd is another good example.

    Another example is Postfix, which uses multiple daemons that communicate through pipes, and only one or two of them (which do very little except accept or emit bytes) run as root and the rest run at a lower privilege.

    ReplyDelete
  3. Two other simple possibilities:

    There is an old (unfashionable) solution to the "a daemon that binds on a low port and hands control to your daemon". It's called inetd (or xinetd). The cons are:


    your daemon needs to talk on stdin/stdout (if you don't control the daemon -- if you don't have the source -- then this is perhaps a showstopper, although some services may have an inetd-compatibility flag)
    a new daemon process is forked for every connection
    it's one extra link in the chain


    Pros:


    available on any old UNIX
    once your sysadmin has set up the config, you're good to go about your development (when you re-build your daemon, might you lose setcap capabilities? And then you'll have to go back to your admin "please sir...")
    daemon doesn't have to worry about that networking stuff, just has to talk on stdin/stdout
    can configure to execute your daemon as a non-root user, as requested


    Another alternative: a hacked-up proxy (netcat or even something more robust) from the privileged port to some arbitrary high-numbered port where you can run your target daemon. (Netcat is obviously not a production solution, but "just my dev box", right?). This way you could continue to use a network-capable version of your server, would only need root/sudo to start proxy (at boot), wouldn't be relying on complex/potentially fragile capabilities.

    ReplyDelete
  4. Linux supports capabilities to support more fine-grained permissions than just "this application is run as root". One of those capabilities is CAP_NET_BIND_SERVICE which is about binding to a privileged port (<1024).

    Unfortunately I don't know how to exploit that to run an application as non-root while still giving it CAP_NET_BIND_SERVICE (probably using setcap, but there's bound to be an existing solution for this).

    ReplyDelete
  5. You can do a port redirect. This is what I do for a Silverlight policy server running on a Linux box

    iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300

    ReplyDelete
  6. My "standard workaround" uses socat as the user-space redirector:

    socat tcp6-listen:80,fork tcp6:8080


    Beware that this won't scale, forking is expensive but it's the way socat works.

    ReplyDelete
  7. File capabilities are not ideal, because they can break after a package update.

    The ideal solution, IMHO, should be an ability to create a shell with inheritable CAP_NET_BIND_SERVICE set.

    Here's a somewhat convoluted way to do this:

    sg $DAEMONUSER "capsh --keep=1 --uid=`id -u $DAEMONUSER` \
    --caps='cap_net_bind_service+pei' -- \
    YOUR_COMMAND_GOES_HERE"


    capsh utility can be found in libcap2-bin package in Debian/Ubuntu distributions. Here's what goes on:


    sg changes effective group ID to that of the daemon user. This is necessary because capsh leaves GID unchanged and we definitely do not want it.
    Sets bit 'keep capabilities on UID change'.
    Changes UID to $DAEMONUSER
    Drops all caps (at this moment all caps are still present because of --keep=1), except inheritable cap_net_bind_service
    Executes your command ('--' is a separator)


    The result is a process with specified user and group, and cap_net_bind_service privileges.

    As an example, a line from ejabberd startup script:

    sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"

    ReplyDelete
  8. systemd is a sysvinit replacement which has an option to launch a daemon with specific capabilities. Options Capabilities=, CapabilityBoundingSet= in systemd.exec(5) manpage.

    ReplyDelete
  9. @Gene Vayngrib: See this page about the potential cause of the libjli.so linker complaint:
    http://osdir.com/ml/debian-bugs-rc/2009-10/msg00054.html
    I'm seeing the problem on Fedora 11, with the Sun JDK 1.6.0_18, where /proc is mounted just fine, so it doesn't look like that's the problem on my box. I have not tried other JDKs in that installation yet.

    ReplyDelete
  10. Or patch your kernel and remove the check.

    (Option of last resort, not recommended).

    ReplyDelete