Is there a way for non-root processes to bind to “privileged” ports on Linux?

后端 未结 24 1291
予麋鹿
予麋鹿 2020-11-22 02:04

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 non

相关标签:
24条回答
  • 2020-11-22 02:59

    I know this is an old question, but now with recent (>= 4.3) kernels there is finally a good answer to this - ambient capabilities.

    The quick answer is to grab a copy of the latest (as-yet-unreleased) version of libcap from git and compile it. Copy the resulting progs/capsh binary somewhere (/usr/local/bin is a good choice). Then, as root, start your program with

    /usr/local/bin/capsh --keep=1 --user='your-service-user-name' \
        --inh='cap_net_bind_service' --addamb='cap_net_bind_service' \ 
        -- -c 'your-program'
    

    In order, we are

    • Declaring that when we switch users, we want to keep our current capability sets
    • Switching user & group to 'your-service-user-name'
    • Adding the cap_net_bind_service capability to the inherited & ambient sets
    • Forking bash -c 'your-command' (since capsh automatically starts bash with the arguments after --)

    There's a lot going on under the hood here.

    Firstly, we are running as root, so by default, we get a full set of capabilities. Included in this is the ability to switch uid & gid with the setuid and setgid syscalls. However, ordinarily when a program does this, it loses its set of capabilities - this is so that the old way of dropping root with setuid still works. The --keep=1 flag tells capsh to issue the prctl(PR_SET_KEEPCAPS) syscall, which disables the dropping of capabilities when changing user. The actual changing of users by capsh happens with the --user flag, which runs setuid and setgid.

    The next problem we need to solve is how to set capabilities in a way that carries on after we exec our children. The capabilities system has always had an 'inherited' set of capabilities, which is " a set of capabilities preserved across an execve(2)" [capabilities(7)]. Whilst this sounds like it solves our problem (just set the cap_net_bind_service capability to inherited, right?), this actually only applies for privileged processes - and our process is not privileged anymore, because we already changed user (with the --user flag).

    The new ambient capability set works around this problem - it is "a set of capabilities that are preserved across an execve(2) of a program that is not privileged." By putting cap_net_bind_service in the ambient set, when capsh exec's our server program, our program will inherit this capability and be able to bind listeners to low ports.

    If you're interested to learn more, the capabilities manual page explains this in great detail. Running capsh through strace is also very informative!

    0 讨论(0)
  • 2020-11-22 03:01

    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:

    1. You will need at least a 2.6.24 kernel
    2. 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.
    3. Linux will disable LD_LIBRARY_PATH on any program that has elevated privileges like setcap or suid. So if your program uses its own .../lib/, you might have to look into another option like port forwarding.

    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.
    • setcap man page
    • "Bind ports below 1024 without root on GNU/Linux": The document that first pointed me towards setcap.

    Note: RHEL first added this in v6.

    0 讨论(0)
  • 2020-11-22 03:01

    Answer at 2015/Sep:

    ip6tables now supports IPV6 NAT: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

    You will need kernel 3.7+

    Proof:

    [09:09:23] root@X:~ ip6tables -t nat -vnL
    Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
     pkts bytes target     prot opt in     out     source               destination
        0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:80 redir ports 8080
        0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:443 redir ports 1443
    
    Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
     pkts bytes target     prot opt in     out     source               destination
    
    Chain OUTPUT (policy ACCEPT 6148 packets, 534K bytes)
     pkts bytes target     prot opt in     out     source               destination
    
    Chain POSTROUTING (policy ACCEPT 6148 packets, 534K bytes)
     pkts bytes target     prot opt in     out     source               destination
    
    0 讨论(0)
  • 2020-11-22 03:03

    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"
    
    0 讨论(0)
  • 2020-11-22 03:04

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

    0 讨论(0)
  • 2020-11-22 03:06

    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
    
    0 讨论(0)
提交回复
热议问题