代码之家  ›  专栏  ›  技术社区  ›  Jason C

在Linux上,有没有办法让非根进程绑定到“特权”端口?

  •  346
  • Jason C  · 技术社区  · 16 年前

    当除了我之外再也没有其他用户的时候,在我的开发框上有这样的限制是非常烦人的。

    我知道 the standard workarounds ,但没有一个能完全满足我的要求:

    1. authbind
    2. Using the iptables REDIRECT target to redirect a low port to a high port (ip6tables(iptables的IPv6版本)尚未实现“nat”表)
    3. sudo(以root身份运行是我试图避免的)
    4. SELinux(或类似产品)。(这只是我的开发工具,我不想引入太多额外的复杂性。)

    sysctl 变量,允许非根进程绑定到Linux上的“特权”端口(小于1024个端口),还是我运气不好?

    use capabilities 这样做。

    22 回复  |  直到 7 年前
        1
  •  437
  •   Cristian Ciupitu    10 年前

    好的,感谢那些指出系统功能的人 CAP_NET_BIND_SERVICE 能力。如果您有一个最近的内核,那么确实可以使用它以非根端口但绑定低端口的形式启动服务。简单的回答是,你做到了:

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

    program 之后执行,它将具有 CAP_NET_BIND_服务 setcap libcap2-bin .

    现在要注意的是:

    1. 如果您的文件是脚本,则此操作无效。(即,使用#!行启动解释器)。在这种情况下,据我所知,您必须将该功能应用于解释器可执行文件本身,这当然是一个安全噩梦,因为任何使用该解释器的程序都将具有该功能。我找不到任何干净、简单的方法来解决这个问题。
    2. 程序 它具有提升的特权,比如 集帽 suid . 所以如果你的 .../lib/ ,您可能需要研究另一个选项,如端口转发。

    注: RHEL first added this in v6 .

        2
  •  44
  •   FlappySocks    15 年前

    iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300
    
        3
  •  32
  •   erlando    16 年前

    另一个例子是Postfix,它使用多个通过管道进行通信的守护进程,其中只有一个或两个(除了接受或发出字节外几乎没有其他功能)作为根运行,其余的以较低的权限运行。

        4
  •  30
  •   Walery Strauch Lia    6 年前

    2017年更新:

    使用 authbind


    比CAP_NET_BIND_服务或定制内核要好得多。


    • Authbind将信任授予 用户/组,并提供对每个端口访问的控制,以及 同时支持IPv4和IPv6(最近添加了IPv6支持)。

      1. 安装: apt-get install authbind

      2. 为所有用户和组配置对相关端口的访问,例如80和443:

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

      3. authbind
        --deep

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

        例如

        authbind --deep java -jar SomeServer.jar
        

    作为Joshua关于破解内核的绝妙建议(=除非您知道自己在做什么,否则不推荐)的后续建议:

    here

    易于理解的对于普通内核或旧内核,您不需要。

    正如其他人所指出的,CAP_NET_BIND_服务也可以完成这项工作。

    e、 对于Java,您必须将其应用于Java JVM

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


    mono/.NET的Dito。



    没有人说你必须运行一个普通的内核,所以你可以自己运行。

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

    你看这条线

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

    并将其更改为

    #define PROT_SOCK 0
    

    如果不希望出现不安全的ssh情况,请将其更改为:

    通常,我会使用您需要的最低设置,例如http为79,在端口25上使用SMTP时为24。

    就这些了。
    编译内核并安装它。


    下面是编译内核的方法:

    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 ..
    

    简言之,如果您想保持安全,请使用iptables;如果您想确保此限制不再困扰您,请编译内核。

        5
  •  30
  •   Joshua    6 年前

    或者修补内核并删除检查。

    (最后选择,不推荐)。

    在里面 net/ipv4/af_inet.c

          if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
                  goto out;
    

    内核将不再检查特权端口。

        6
  •  25
  •   Gabriel Burt    11 年前

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

    这具有使用脚本服务器的优点,而且非常简单。

        7
  •  25
  •   urusha    6 年前

    出于某种原因,没有人提到将sysctl net.ipv4.ip_unprivileged_port_start降低到您需要的值。

    sysctl net.ipv4.ip_unprivileged_port_start=443
    

    有人可能会说,存在一个潜在的安全问题:非特权用户现在可能会绑定到其他特权端口(444-1024)。 但是,通过阻止其他端口,您可以使用iptables轻松解决此问题:

    iptables -I INPUT -p tcp --dport 444:1024 -j DROP
    iptables -I INPUT -p udp --dport 444:1024 -j DROP
    

    • 从某种意义上讲,它(IMO)甚至比设置CAP_NET_BIND_SERVICE/setuid更安全,因为应用程序根本不需要setuid,甚至部分不需要setuid(功能实际上是)。 例如,要捕获支持功能的应用程序的coredump,您需要更改sysctl fs.suid_dumpable(这会导致另一个潜在的安全问题)
    • 存在安全缺陷:当您的应用程序(或任何使用端口443-1024的应用程序)因某种原因关闭时,另一个应用程序可能会占用该端口。但这个问题也可以应用于CAP/suid(如果您在解释器上设置它,例如java/nodejs)和iptables重定向。使用systemd套接字方法排除此问题。使用authbind方法仅允许特殊用户绑定。
    • 不需要每次部署新版本的应用程序时都设置CAP/suid。
    • 不需要内核重建(如果运行的版本支持此sysctl设置)
    • 过度执行iptables REDIRECT/DNAT方法,因为它不需要地址转换、连接状态跟踪等。这仅在高负载系统上才明显。

    根据情况,我将在sysctl、CAP、authbind和iptables重定向之间进行选择。这是伟大的,我们有这么多的选择。

        8
  •  19
  •   Cyberax    13 年前

    文件功能并不理想,因为它们在包更新后可能会中断。

    理想的解决方案IMHO应该是能够创建具有可继承性的shell CAP_NET_BIND_SERVICE 设置

    下面是一个有点复杂的方法:

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

    capsh 该实用程序可以在Debian/Ubuntu发行版的libcap2 bin包中找到。事情是这样的:

    • sg 卡什
    • 将UID更改为 $DAEMONUSER
    • 放下所有盖子(此时所有盖子仍然存在,因为 --keep=1 cap_net_bind_service

    结果是具有指定用户和组的进程,以及 cap_net_bind_服务 特权。

    ejabberd

    sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"
    
        9
  •  19
  •   KJ Tsanaktsidis    8 年前

    快速的答案是获取一份最新(尚未发布)版本的libcap from git 并编译它。复制结果 progs/capsh 某处二进制( /usr/local/bin

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

    按顺序,我们是

    • 声明当我们切换用户时,我们希望保留当前的功能集
    • 交换用户&组到“您的服务用户名”
    • 添加 cap_net_bind_service 对继承的&环境设定
    • bash -c 'your-command' (自 capsh 使用后面的参数自动启动bash --

    这里有很多事情要做。

    首先,我们以root用户身份运行,因此默认情况下,我们获得了一整套功能。其中包括切换uid&吉德 setuid setgid 系统调用。然而,通常当一个程序执行此操作时,它会丢失它的一组功能-这就是使用 设置用户标识符 --keep=1 旗帜告诉我们 卡什 prctl(PR_SET_KEEPCAPS) 卡什 发生在 --user 旗帜,它运行 塞吉德 .

    我们需要解决的下一个问题是,如何在我们完成任务后继续设置功能 exec capabilities(7) ]。虽然这听起来似乎解决了我们的问题(只需设置 cap_net_bind_服务 继承的功能,对吗?),这实际上只适用于特权进程——而我们的进程不再是特权进程,因为我们已经改变了用户(使用 --使用者 旗帜)。

    cap_net_bind_服务 在环境设置中,当 卡什

    如果您有兴趣了解更多信息,请访问 manual page 详细解释了这一点。跑步 卡什 strace 信息量也很大!

        10
  •  19
  •   Gene McCulley    5 年前

    /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0 .

        11
  •  16
  •   Martin Carpenter    16 年前

    另外两种简单的可能性:

    对于“绑定到低端端口并将控制权交给您的守护进程”的守护进程,有一个古老的(不流行的)解决方案。它被称为inetd(或xinetd)。缺点是:

    • 您的守护进程需要在stdin/stdout上进行对话(如果您不控制守护进程——如果您没有源代码——那么这可能是一个showtupper,尽管某些服务可能有inetd兼容标志)
    • 每个连接都有一个新的守护进程
    • 这是链条中的一个额外环节

    赞成的意见:

    • 可在任何旧UNIX上使用
    • 一旦你的系统管理员设置了配置,你就可以开始你的开发了(当你重新构建守护程序时,你可能会失去setcap功能吗?然后你必须回到你的管理员“请先生…”
    • 可以配置为根据请求以非根用户身份执行守护程序

    更健壮

        12
  •  15
  •   Astro    15 年前

    我的“标准解决方案”使用socat作为用户空间重定向器:

    socat tcp6-listen:80,fork tcp6:8080
    

    注意这不会扩展,分叉很昂贵,但这是socat的工作方式。

        13
  •  14
  •   Cristian Ciupitu    10 年前

    Linux支持 capabilities 支持比“此应用程序以根用户身份运行”更细粒度的权限。这些能力之一是 CAP_NET_BIND_SERVICE 这是关于绑定到特权端口(<1024)的。

    CAP_NET_BIND_服务 (可能使用 setcap

        14
  •  13
  •   Community kfsone    7 年前

    TLDR:对于“答案”(在我看来),跳到>&燃气轮机;TLDR<&书信电报;在这个答案中加入一部分。

    好的,我已经弄明白了(这次是真的),这个问题的答案,我的这个答案也是为我的晋升道歉的一种方式 another answer (无论是在这里还是在推特上)我认为是“最好的”,但在尝试之后,发现我错了。从我的错误中学习孩子们:在你自己真正尝试之前,不要推销任何东西!

    一些 systemd 带着它 Capabilities= CapabilitiesBindingSet= 设置。经过一段时间的努力,我发现这不是解决办法 因为:

    正如OP明智地指出的那样,它是 最好避免这种情况(如果可能的话,针对所有守护进程!)。

    您不能将与功能相关的选项用于 User= Group= 在里面 系统的 重置时间 execev 系统的 分叉并放下烫发,功能重置。没有办法解决这个问题,内核中的所有绑定逻辑都是uid=0的基本逻辑,而不是功能。这意味着功能不太可能是这个问题的正确答案(至少在不久的任何时候)。顺便提一句 setcap 正如其他人所提到的,这不是一个解决办法。它对我不起作用,它不能很好地与脚本一起工作,而且无论何时文件更改,这些脚本都会被重置。

    iptables 建议(OP也提到)是“第二个最好的解决方案”-P

    >>TLDR<&书信电报;

    iptables taken from DNSChain

    [Unit]
    Description=dnschain
    After=network.target
    Wants=namecoin.service
    
    [Service]
    ExecStart=/usr/local/bin/dnschain
    Environment=DNSCHAIN_SYSD_VER=0.0.1
    PermissionsStartOnly=true
    ExecStartPre=/sbin/sysctl -w net.ipv4.ip_forward=1
    ExecStartPre=-/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
    ExecStartPre=-/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
    ExecStartPre=/sbin/iptables -A INPUT -p udp --dport 5333 -j ACCEPT
    ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
    ExecStopPost=/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
    ExecStopPost=/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
    User=dns
    Group=dns
    Restart=always
    RestartSec=5
    WorkingDirectory=/home/dns
    PrivateTmp=true
    NoNewPrivileges=true
    ReadOnlyDirectories=/etc
    
    # Unfortunately, capabilities are basically worthless because they're designed to restrict root daemons. Instead, we use iptables to listen on privileged ports.
    # Capabilities=cap_net_bind_service+pei
    # SecureBits=keep-caps
    
    [Install]
    WantedBy=multi-user.target
    

    在这里,我们完成了以下工作:

    • 我们可以将命令包含在单元文件本身中,这样就省去了人们的麻烦。 系统的 为我们清理防火墙规则,确保在守护进程未运行时删除它们。
    • 系统的 声明),假定即使守护进程被破坏并设置 uid=0 .

    iptables 不幸的是,它仍然是一个丑陋且难以使用的实用程序。如果守护进程正在侦听 eth0:0 eth0 slightly different .

        15
  •  12
  •   j123b567    8 年前

    您可以稍后使用 systemd socket activate .

    不需要任何功能、iptables或其他技巧。

    这是本简单示例中相关systemd文件的内容 python http server

    文件 httpd-true.service

    [Unit]
    Description=Httpd true 
    
    [Service]
    ExecStart=/usr/local/bin/httpd-true
    User=subsonic
    
    PrivateTmp=yes
    

    httpd-true.socket

    [Unit]
    Description=HTTPD true
    
    [Socket]
    ListenStream=80
    
    [Install]
    WantedBy=default.target
    
        16
  •  11
  •   zbyszek    13 年前

    systemd 是一个sysvinit替代品,它可以选择启动具有特定功能的守护进程。选项功能=,CapabilityBoundingSet=in systemd.exec(5)

        17
  •  10
  •   00500005    9 年前

    端口重定向对我们来说最有意义,但我们遇到了一个问题,我们的应用程序将在本地解析一个url,该url也需要重新路由;(这意味着你 shindig ).

    这还允许您在访问本地计算机上的url时重定向。

    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
    
        18
  •  7
  •   Andrew Barber Eric Lafortune    11 年前

    启动时:

    iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
    

        19
  •  4
  •   evandrix    4 年前

    还有“djb方式”。您可以使用此方法以root用户身份在tcpserver下的任何端口上启动流程,然后它将在流程启动后立即将流程的控制权交给您指定的用户。

    #!/bin/sh
    
    UID=$(id -u username)
    GID=$(id -g username)
    exec tcpserver -u "${UID}" -g "${GID}" -RHl0 0 port /path/to/binary &
    

    http://thedjbway.b0llix.net/daemontools/uidgid.html

        20
  •  3
  •   Stefan Steiger Marco van de Voort    10 年前

    使用 privbind 实用程序:它允许非特权应用程序绑定到保留端口。

        21
  •  3
  •   assayag.org    4 年前

    sudo iptables -t nat -A OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080
    sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
    

    然后以普通用户身份在8080端口上运行程序。

    然后,您将能够访问 http://127.0.0.1 80

        22
  •  1
  •   duanev    10 年前

    由于OP只是开发/测试,因此不太圆滑的解决方案可能会有所帮助:

    setcap可用于脚本的解释器,以向脚本授予功能。如果不接受全局解释器二进制文件上的setcaps,则制作二进制文件的本地副本(任何用户都可以),并在该副本上获得setcap的root。Python2(至少)可以与脚本开发树中解释器的本地副本一起正常工作。不需要suid,因此root用户可以控制用户可以访问哪些功能。

    #!/bin/sh
    #
    #  Watch for updates to the Python2 interpreter
    
    PRG=python_net_raw
    PRG_ORIG=/usr/bin/python2.7
    
    cmp $PRG_ORIG $PRG || {
        echo ""
        echo "***** $PRG_ORIG has been updated *****"
        echo "Run the following commands to refresh $PRG:"
        echo ""
        echo "    $ cp $PRG_ORIG $PRG"
        echo "    # setcap cap_net_raw+ep $PRG"
        echo ""
        exit
    }
    
    ./$PRG $*
    
        23
  •  1
  •   Craig McQueen Dr. Watson    7 年前

    wasn't supported for IPv6 . 但显然,它现在在ip6tables v1.4.18和Linux内核v3.8中得到了支持。

    我还发现预路由重定向对于在机器内启动的连接不起作用。要处理来自本地计算机的连接,请添加输出规则,另请参见 iptables port redirect not working for localhost

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

    我还发现预路由重定向 also affects forwarded packets -m addrtype --dst-type LOCAL . 例如,类似于:

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

    socat :

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

    然而,该方法的一个缺点是,侦听端口8080的应用程序不知道传入连接的源地址(例如,出于日志记录或其他识别目的)。

        24
  •  0
  •   HVNSweeting    9 年前

    2015年9月的答复:

    ip6tables现在支持IPV6 NAT: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

    您将需要内核3.7+

    [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