当前位置:  开发笔记 > 运维 > 正文

有没有办法让非root进程绑定到Linux上的"特权"端口?

如何解决《有没有办法让非root进程绑定到Linux上的"特权"端口?》经验,为你挑选了18个好方法。

在我的开发盒上有这个限制是非常烦人的,因为除了我之外不会有任何用户.

我知道标准的解决方法,但它们都没有完全符合我的要求:

    authbind(Debian测试中的版本,1.0,仅支持IPv4)

    使用iptables REDIRECT目标将低端口重定向到高端口(对于ip6tables,iptables的IPv6版本尚未实现"nat"表)

    sudo(以root身份运行是我想避免的)

    SELinux(或类似).(这只是我的开发盒,我不想引入很多额外的复杂性.)

是否有一些简单的sysctl变量允许非root进程绑定到Linux上的"特权"端口(端口小于1024),或者我只是运气不好?

编辑:在某些情况下,您可以使用功能来执行此操作.



1> Jason Creigh..:

好的,感谢那些指出功能系统和CAP_NET_BIND_SERVICE功能的人.如果您有最新的内核,确实可以使用它来启动非root用户服务但绑定低端口.简短的回答是你这样做:

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

然后随时program执行它将具有该CAP_NET_BIND_SERVICE功能.setcap在debian包中libcap2-bin.

现在注意事项:

    您至少需要2.6.24内核

    如果您的文件是脚本,这将不起作用.(即,使用#!行启动解释器).在这种情况下,据我所知,您必须将该功能应用于解释器可执行文件本身,这当然是一个安全噩梦,因为使用该解释器的任何程序都具有该功能.我无法找到任何干净,简单的方法来解决这个问题.

    Linux将禁用任何program具有提升权限的LD_LIBRARY_PATH,例如setcapsuid.因此,如果您program使用自己的.../lib/,您可能需要查看另一个选项,如端口转发.

资源:

能力(7)手册页.如果要在生产环境中使用功能,请仔细阅读这篇文章.关于如何通过exec()调用继承功能有一些非常棘手的细节.

setcap手册页

"在GNU/Linux上没有root的情况下绑定低于1024的端口":首先指向我的文档setcap.

注意:RHEL首先在v6中添加了这个.


setcap设置是否会在重新启动后持续存在; 如果不存在这个规则的标准位置,那么它是否在系统启动期间运行?Debian/Ubuntu上的`/ etc/security/capability.conf`有什么帮助吗?
脚本的部分解决方法:创建解释器的副本(例如bash),为其提供功能,但将访问权限限制为需要它的用户.当然,这些用户必须是可信任的,但无论如何他们都可以更改脚本.
除了前面提到的debian(二进制)包,开发人员的网站是http://www.friedhoff.org/posixfilecaps.html相关论文/演示文稿/等...

2> FlappySocks..:

您可以执行端口重定向.这就是我在Linux机器上运行的Silverlight策略服务器所做的

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


@zbyszek这可以用于本地机器连接,还有一个额外的iptables规则:http://stackoverflow.com/a/31795603/1356953
不幸的是,这只适用于路由连接,即不适用于本地机器.

3> Paul Tomblin..:

该标准的办法就是让他们"setuid的",使他们开始了为根,然后他们马上扔掉root特权,因为他们已经绑定的端口,但他们开始接受它连接之前.您可以在Apache和INN的源代码中看到这方面的好例子.我被告知Lighttpd是另一个很好的例子.

另一个例子是后缀,它使用通过管道进行通信,以及多个守护进程只有一个或它们中的两(其中做很少除了接收或发射字节)作为根,其余运行在较低的特权运行.


如果程序是用解释语言或字节码解释器(如C#(Mono),Java,Python)编写的话,这很难.(显然Perl通过[`binfmt_misc`](https://www.kernel.org/doc/Documentation/binfmt_misc.txt)及其'C'标志完成了它;我不确定其他人.)

4> Joshua..:

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

(最后的选择,不推荐).

net/ipv4/af_inet.c,删除读取的两行

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

并且内核将不再检查特权端口.


这是个坏主意.但实际上会有效.肯定让我开怀大笑.
当然这是个坏主意.这就是为什么我说最后的手段.开源的关键是如果它不能按你想要的方式工作就可以改变它.
出于多种原因,这是一个非常糟糕的想法.
我不确定你为什么被投票.恶意软件编写者不关心他们监听什么端口,并且他们非常乐意打开大于1024的端口.在我看来,所有端口都应该需要特权,或者没有端口应该需要特权.并且要求root打开1024以下的端口只意味着你有一个以root身份运行的高风险应用程序.这对我来说似乎是一个非常愚蠢的想法.也许我在这里遗漏了一些东西......
回到当天,在UNIX到UNIX协议中使用了端口<1024来证明另一端运行的代码是以root身份运行的.这对于具有共同安全性的一组UNIX服务器来说相当不错.
这不是一个坏主意.现代世界不像UNIX那样在70年代发挥作用,正如jww所指出的那样,它防止的安全问题数量(几乎没有)不值得强迫Web服务器等以root身份运行,即使它们正确地删除了特权.有一个[很好解释为什么1024限制在这里已经过时](http://www.staldal.nu/tech/2007/10/31/why-can-only-root-listen-to-ports-below-1024 /).

5> Gabriel Burt..:

您可以设置本地SSH隧道,例如,如果您希望端口80命中您的应用程序绑定到3000:

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

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


这可以使用没有ssh开销的netcat来完成:`sudo nc -l 80 | nc localhost 3000`

6> Cyberax..:

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

理想的解决方案,恕我直言,应该是一个创建具有可继承CAP_NET_BIND_SERVICE集的shell的能力.

这是一个有点复杂的方法:

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将有效组ID更改为守护程序用户的ID.这是必要的,因为capsh保持GID不变,我们绝对不希望它.

设置位'保持UID更改功能'.

将UID更改为 $DAEMONUSER

丢弃所有上限(此时所有上限仍然存在--keep=1),除了可继承cap_net_bind_service

执行你的命令(' - '是一个分隔符)

结果是具有指定用户和组以及cap_net_bind_service权限的进程.

例如,ejabberd启动脚本中的一行:

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



7> Stefan Steig..:

2017年更新:

使用authbind
比CAP_NET_BIND_SERVICE或自定义内核好多了.

CAP_NET_BIND_SERVICE授予对二进制文件的信任,但不提供对每个端口访问的控制.

Authbind向用户/组授予信任并提供对每端口访问的控制,并支持IPv4和IPv6(最近添加了IPv6支持).

    安装: apt-get install authbind

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

    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

    通过authbind
    (可选指定--deep或其他参数,请参见手册页)执行命令:

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

    例如

    authbind --deep java -jar SomeServer.jar
    


作为约书亚神话般的后续行动(=不推荐,除非你知道你做了什么)建议破解内核:

我先在这里发布.

简单.使用普通或旧内核,您不需要.
正如其他人所指出的,iptables可以转发一个端口.
正如其他人所指出的,CAP_NET_BIND_SERVICE也可以完成这项工作.
当然,如果从脚本启动程序,CAP_NET_BIND_SERVICE将失败,除非你在shell解释器上设置上限,这是没有意义的,你也可以以root身份运行你的服务...
例如对于Java,你必须应用它到JAVA JVM

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

显然,这意味着任何Java程序都可以绑定系统端口.
Dito为mono/.NET.

我也很确定xinetd不是最好的想法.
但由于这两种方法都是黑客攻击,为什么不通过解除限制来解除限制呢?
没有人说你必须运行一个普通的内核,所以你可以运行自己的内核.

您只需下载最新内核的源代码(或您目前拥有的内核).然后,你去:

/usr/src/linux-/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情况,你可以改成它:#define PROT_SOCK 24

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

这已经是全部了.
编译内核,然后安装它.
重启.
完成 - 这个愚蠢的限制是GONE,这也适用于脚本.

以下是编译内核的方法:

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-.tar.bz2
cd linux-source-

# 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  - 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,如果你想确保这个限制再也不会困扰你,请编译内核.


修改内核会使未来的更新变得更加痛苦.我不会这样做,因为我知道我会懒得不断定期更新我的内核.它在理论上是可能的,但在很多情况下它不是一个可行的选择.

8> Martin Carpe..:

另外两个简单的可能性

有一个旧的(不合时宜的)解决方案是"一个守护程序,它绑定在一个低端口并将控制权交给你的守护进程".它被称为inetd(或xinetd).缺点是:

您的守护进程需要在stdin/stdout上进行通信(如果您不控制守护进程 - 如果您没有源代码 - 那么这可能是一个showstopper,尽管某些服务可能具有inetd兼容性标志)

为每个连接分叉一个新的守护进程

它是链条中的一个额外链接

优点:

在任何旧UNIX上都可用

一旦你的系统管理员设置了配置,你就可以开始进行开发(当你重新构建你的守护进程时,你可能会失去setcap功能吗?然后你将不得不回到你的管理员那里"请先生...... ".)

守护进程不必担心网络的东西,只需要谈谈stdin/stdout

可以根据请求配置为以非root用户身份执行守护程序

另一个替代方案:从特权端口到一些任意高编号端口的hacked-up代理(netcat甚至更强大的代理),您可以在其中运行目标守护程序.(Netcat显然不是一个生产解决方案,但"只是我的开发盒",对吧?).这样,您可以继续使用支持网络的服务器版本,只需要root/sudo启动代理(启动时),不会依赖复杂/可能脆弱的功能.



9> Astro..:

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

socat tcp6-listen:80,fork tcp6:8080

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



10> Joachim Saue..:

Linux支持的功能支持更细粒度的权限,而不仅仅是"此应用程序以root身份运行".其中一项功能是CAP_NET_BIND_SERVICE绑定到特权端口(<1024).

不幸的是,我不知道如何利用它来运行非root用户同时仍然给它CAP_NET_BIND_SERVICE(可能使用setcap,但必然会有一个现有的解决方案).



11> KJ Tsanaktsi..:

我知道这是一个老问题,但现在最近(> = 4.3)内核终于有了一个很好的答案 - 环境功能.

快速回答是从git中获取最新(尚未发布)的libcap版本并进行编译.将生成的progs/capsh二进制文件复制到某处(/usr/local/bin是一个不错的选择).然后,以root身份启动您的程序

/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身份运行,因此默认情况下,我们会获得一整套功能.其中包括使用setuidsetgid系统调用切换uid和gid的功能.但是,通常当一个程序执行此操作时,它会丢失其功能集 - 这样就可以setuid保留原有的root方法.该--keep=1标志告诉capsh发出prctl(PR_SET_KEEPCAPS)syscall,它在更改用户时禁用功能丢弃.用户的实际变化通过capsh与发生的--user标志,它运行setuidsetgid.

我们需要解决的下一个问题是如何以我们exec的孩子之后的方式设置能力.功能系统始终具有"继承"的一组功能,这是"在execve(2)中保留的一组功能"[ capabilities(7) ].虽然这听起来像是解决了我们的问题(只是设置了cap_net_bind_service继承的能力,对吗?),但这实际上只适用于特权进程 - 而且我们的进程不再具有特权,因为我们已经更改了用户(带有--user标志).

新的环境功能集解决了这个问题 - 它是"一组在非特权程序的execve(2)中保留的功能." 通过放入cap_net_bind_service环境集,当capshexec是我们的服务器程序时,我们的程序将继承此功能并能够将侦听器绑定到低端口.

如果您有兴趣了解更多信息,功能手册页将详细解释这一点.运行capsh经过strace也非常丰富!



12> urusha..:

出于某种原因,没有人提到将sysctl net.ipv4.ip_unprivileged_port_start降低到所需的值.示例:我们需要将应用程序绑定到443端口.

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,甚至部分(功能实际上是).例如,要捕获启用功能的应用程序的coredump,您需要更改sysctl fs.suid_dumpable(这会导致另一个潜在的安全问题)另外,当设置CAP/suid时,/ proc/PID目录由root拥有,所以您的非root用户将无法获得运行进程的完整信息/控制权,例如,用户将无法(通常情况下)通过/ proc/PID/fd /确定哪些连接属于应用程序(netstat -aptn | grep PID).

有安全劣势:当您的应用程序(或任何使用端口443-1024的应用程序)由于某种原因而关闭时,另一个应用程序可能会占用该端口.但是这个问题也可以应用于CAP/suid(如果你在解释器上设置它,例如java/nodejs)和iptables-redirect.使用systemd-socket方法排除此问题.使用authbind方法仅允许特殊用户绑定.

每次部署新版本的应用程序时都不需要设置CAP/suid.

不需要应用程序支持/修改,如systemd-socket方法.

不需要内核重建(如果运行版本支持此sysctl设置)

没有像authbind/privbind方法那样执行LD_PRELOAD,这可能会影响性能,安全性和行为(是吗?还没有测试过).在其余的authbind中是非常灵活和安全的方法.

过度执行iptables REDIRECT/DNAT方法,因为它不需要地址转换,连接状态跟踪等.这在高负载系统上才会显着.

根据具体情况,我会在sysctl,CAP,authbind和iptables-redirect之间进行选择.我们有很多选择,这很棒.


感谢您的出色回答!似乎该功能首次出现在[2017年4月的Linux 4.11](https://kernelnewbies.org/Linux_4.11)中,所以在2009年我第一次提出这个问题时就没有出现。我也进行了快速测试,即使sysctl名称中包含“ ipv4”,它似乎也适用于IPV6。

13> Greg Slepak..:

TLDR:对于"答案"(如我所见),请跳到本答案的>> TLDR <<部分.

好吧,我已经弄明白了(这次是真的),这个问题的答案,我的这个答案也是一种道歉的方式,以促进另一个答案(在这里和在推特上)我认为是"最好的"但是在尝试之后,发现我错了.从我的错误中学习孩子:在你真正尝试过之前不要宣传一些东西!

我再次回顾了这里的所有答案.我尝试了其中一些(并选择不尝试其他人,因为我根本不喜欢这些解决方案).我认为解决方案是使用systemdCapabilities=CapabilitiesBindingSet=设置.经过一段时间的摔跤后,我发现这不是解决方案,因为:

功能旨在限制根进程!

由于OP明智地指出,这是总是最好避免(对所有的守护程序如果可能的话!).

您不能使用选项与相关的功能User=,并Group=systemd单元文件,因为能力总是当复位execev(或任何功能)被调用.换句话说,当systemd分叉并丢弃其权限时,功能将被重置.没有办法解决这个问题,内核中的所有绑定逻辑都是基于uid = 0的基础,而不是功能.这意味着Capabilities不太可能成为这个问题的正确答案(至少在很短的时间内).顺便提一下,setcap正如其他人所提到的,不是解决方案.它对我不起作用,它对脚本不起作用,并且无论何时文件发生变化都会重置.

在我微薄的辩护中,我确实陈述了(在我现在删除的评论中),詹姆斯的iptables建议(OP也提到了),是"第二好的解决方案".:-P

>> TLDR <<

解决方案是systemd与动态iptables命令结合使用,如下所示(取自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

在这里我们完成以下任务:

守护进程监听5333,但是由于53,成功接受了连接 iptables

我们可以将命令包含在单元文件本身中,从而节省人们的头痛.systemd清除我们的防火墙规则,确保在守护程序未运行时删除它们.

我们从未以root身份运行,并且我们无法进行权限提升(至少systemd声称),即使守护程序被泄露并设置也是如此uid=0.

iptables不幸的是,它仍然是一个非常丑陋且难以使用的实用程序.例如,如果守护程序正在侦听,eth0:0而不是eth0命令略有不同.


感谢您指出systemd的功能特性.但是,当systemd直接启动二进制文件(而不是脚本)时,不需要复杂的iptables.设置`AmbientCapabilities = CAP_NET_BIND_SERVICE`对我来说非常好,与`User =`结合使用.
除非他们有一个_really ancient_ Linux发行版,否则任何人都不应该使用旧的别名,例如`eth0:0`.它们已被弃用多年,最终会消失.

14> zbyszek..:

systemd是一个sysvinit替代品,可以选择启动具有特定功能的守护程序.选项Capabilities =,CapabilityBoundingSet =在systemd.exec(5)联机帮助页中.


我以前推荐过这个答案,但在尝试之后,现在我没有.请参阅[我的回答](http://stackoverflow.com/a/21653102/1781435)以获取仍使用`systemd`的替代方案.

15> 00500005..:

端口重定向对我们来说最有意义,但我们遇到了一个问题,我们的应用程序将在本地解析一个也需要重新路由的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



16> MageProspero..:

在启动时:

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

然后,您可以绑定到前进的端口.



17> j123b567..:

使用systemd,您只需稍微修改您的服务即可接受预先激活的套接字.

您可以稍后使用systemd socket activate.

不需要功能,iptables或其他技巧.

这是来自简单python http服务器的这个示例的相关systemd文件的内容

文件 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



18> Gene McCulle..:

现代Linux支持/sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0

推荐阅读
拾味湖
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有