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

后端 未结 24 1288
予麋鹿
予麋鹿 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:40

    Update 2017:

    Use authbind


    Much better than CAP_NET_BIND_SERVICE or a custom kernel.

    • CAP_NET_BIND_SERVICE grants trust to the binary but provides no control over per-port access.
    • Authbind grants trust to the user/group and provides control over per-port access, and supports both IPv4 and IPv6 (IPv6 support has been added as of late).

      1. Install: apt-get install authbind

      2. Configure access to relevant ports, e.g. 80 and 443 for all users and groups:

        sudo touch /etc/authbind/byport/80
        sudo touch /etc/authbind/byport/443
        sudo chmod 777 /etc/authbind/byport/80
        sudo chmod 777 /etc/authbind/byport/443

      3. Execute your command via authbind
        (optionally specifying --deep or other arguments, see the man page):

        authbind --deep /path/to/binary command line args
        

        e.g.

        authbind --deep java -jar SomeServer.jar
        

    As a follow-up to Joshua's fabulous (=not recommended unless you know what you do) recommendation to hack the kernel:

    I've first posted it here.

    Simple. With a normal or old kernel, you don't.
    As pointed out by others, iptables can forward a port.
    As also pointed out by others, CAP_NET_BIND_SERVICE can also do the job.
    Of course CAP_NET_BIND_SERVICE will fail if you launch your program from a script, unless you set the cap on the shell interpreter, which is pointless, you could just as well run your service as root...
    e.g. for Java, you have to apply it to the JAVA JVM

    sudo /sbin/setcap 'cap_net_bind_service=ep' /usr/lib/jvm/java-8-openjdk/jre/bin/java
    

    Obviously, that then means any Java program can bind system ports.
    Dito for mono/.NET.

    I'm also pretty sure xinetd isn't the best of ideas.
    But since both methods are hacks, why not just lift the limit by lifting the restriction ?
    Nobody said you have to run a normal kernel, so you can just run your own.

    You just download the source for the latest kernel (or the same you currently have). Afterwards, you go to:

    /usr/src/linux-<version_number>/include/net/sock.h:
    

    There you look for this line

    /* Sockets 0-1023 can't be bound to unless you are superuser */
    #define PROT_SOCK       1024
    

    and change it to

    #define PROT_SOCK 0
    

    if you don't want to have an insecure ssh situation, you alter it to this: #define PROT_SOCK 24

    Generally, I'd use the lowest setting that you need, e.g 79 for http, or 24 when using SMTP on port 25.

    That's already all.
    Compile the kernel, and install it.
    Reboot.
    Finished - that stupid limit is GONE, and that also works for scripts.

    Here's how you compile a kernel:

    https://help.ubuntu.com/community/Kernel/Compile

    # You can get the kernel-source via package linux-source, no manual download required
    apt-get install linux-source fakeroot
    
    mkdir ~/src
    cd ~/src
    tar xjvf /usr/src/linux-source-<version>.tar.bz2
    cd linux-source-<version>
    
    # Apply the changes to PROT_SOCK define in /include/net/sock.h
    
    # Copy the kernel config file you are currently using
    cp -vi /boot/config-`uname -r` .config
    
    # Install ncurses libary, if you want to run menuconfig
    apt-get install libncurses5 libncurses5-dev
    
    # Run menuconfig (optional)
    make menuconfig
    
    # Define the number of threads you wanna use when compiling (should be <number CPU cores> - 1), e.g. for quad-core
    export CONCURRENCY_LEVEL=3
    # Now compile the custom kernel
    fakeroot make-kpkg --initrd --append-to-version=custom kernel-image kernel-headers
    
    # And wait a long long time
    
    cd ..
    

    In a nutshell, use iptables if you want to stay secure, compile the kernel if you want to be sure this restriction never bothers you again.

    0 讨论(0)
  • 2020-11-22 02:42

    You can setup a local SSH tunnel, eg if you want port 80 to hit your app bound to 3000:

    sudo ssh $USERNAME@localhost -L 80:localhost:3000 -N
    

    This has the advantage of working with script servers, and being very simple.

    0 讨论(0)
  • 2020-11-22 02:42

    Port redirect made the most sense for us, but we ran into an issue where our application would resolve a url locally that also needed to be re-routed; (that means you shindig).

    This will also allow you to be redirected when accessing the url on the local machine.

    iptables -A PREROUTING -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
    iptables -A OUTPUT -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
    
    0 讨论(0)
  • 2020-11-22 02:43

    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.

    0 讨论(0)
  • 2020-11-22 02:44

    I tried the iptables PREROUTING REDIRECT method. In older kernels it seems this type of rule wasn't supported for IPv6. But apparently it is now supported in ip6tables v1.4.18 and Linux kernel v3.8.

    I also found that PREROUTING REDIRECT doesn't work for connections initiated within the machine. To work for conections from the local machine, add an OUTPUT rule also — see iptables port redirect not working for localhost. E.g. something like:

    iptables -t nat -I OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080
    

    I also found that PREROUTING REDIRECT also affects forwarded packets. That is, if the machine is also forwarding packets between interfaces (e.g. if it's acting as a Wi-Fi access point connected to an Ethernet network), then the iptables rule will also catch connected clients' connections to Internet destinations, and redirect them to the machine. That's not what I wanted—I only wanted to redirect connections that were directed to the machine itself. I found I can make it only affect packets addressed to the box, by adding -m addrtype --dst-type LOCAL. E.g. something like:

    iptables -A PREROUTING -t nat -p tcp --dport 80 -m addrtype --dst-type LOCAL -j REDIRECT --to-port 8080
    

    One other possibility is to use TCP port forwarding. E.g. using socat:

    socat TCP4-LISTEN:www,reuseaddr,fork TCP4:localhost:8080
    

    However one disadvantage with that method is, the application that is listening on port 8080 then doesn't know the source address of incoming connections (e.g. for logging or other identification purposes).

    0 讨论(0)
  • 2020-11-22 02:45

    Modern Linux supports /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.

    0 讨论(0)
提交回复
热议问题