在我的开发盒上有这个限制是非常烦人的,因为除了我之外不会有任何用户 .
我知道the standard workarounds,但它们都没有完全符合我的要求:
-
authbind(Debian测试中的版本,1.0,仅支持IPv4)
-
Using the iptables REDIRECT target to redirect a low port to a high port("nat"表尚未实现ip6tables,iptables的IPv6版本)
-
sudo(以root身份运行是我要避免的)
-
SELinux(或类似) . (这只是我的开发盒,我不想引入很多额外的复杂性 . )
是否有一些简单的 sysctl
变量允许非root进程绑定到Linux上的"privileged"端口(端口小于1024),或者我只是运气不好?
编辑:在某些情况下,你可以use capabilities这样做 .
22 回答
由于OP只是开发/测试,不太流行的解决方案可能会有所帮助:
setcap可以在脚本的解释器上使用,以授予脚本功能 . 如果全局解释器二进制文件上的setcaps不可接受,则制作二进制文件的本地副本(任何用户都可以)并在此副本上获取rootto to setcap . Python2(至少)与脚本开发树中的解释器的本地副本一起正常工作 . 不需要suid,因此root用户可以控制用户可以访问的功能 .
如果需要跟踪解释器的系统范围更新,请使用以下shell脚本来运行脚本:
好的,感谢那些指出功能系统和
CAP_NET_BIND_SERVICE
功能的人 . 如果你有一个最新的内核,确实可以使用它来启动非root用户服务但绑定低端口 . 简短的回答是你这样做:然后随时执行
program
它将具有CAP_NET_BIND_SERVICE
功能 .setcap
在debian包中libcap2-bin
.现在注意事项:
您至少需要2.6.24内核
这赢得了'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必须将该功能应用于解释器可执行文件本身,这当然是一个安全噩梦,因为使用该解释器的任何程序都具有该功能 . 我无法找到任何干净,简单的方法来解决这个问题 .
Linux将在具有提升权限的任何
program
上禁用LD_LIBRARY_PATH,例如setcap
或suid
. 因此,如果您的program
使用自己的.../lib/
,您可能需要查看另一个选项,如端口转发 .资源:
capabilities(7) man page . 如果您要在 生产环境 环境中使用功能,请仔细阅读这篇文章 . 有关如何通过exec()调用继承功能的一些非常棘手的细节,详细信息请参见此处 .
setcap man page
"Bind ports below 1024 without root on GNU/Linux":首先向我指出
setcap
的文件 .注意:RHEL first added this in v6 .
标准方法是将它们设置为“setuid”,以便它们以root身份启动,然后一旦它们绑定到端口但在它们开始接受与它的连接之前,它们就会丢弃该root权限 . 你可以在Apache和INN的源代码中看到很好的例子 . 我被告知Lighttpd是另一个很好的例子 .
另一个例子是Postfix,它使用通过管道进行通信的多个守护进程,并且只有一个或两个(除了接受或发出字节之外几乎没有)以root身份运行,其余守护进程以较低权限运行 .
您可以执行端口重定向 . 这就是我在Linux机器上运行的Silverlight策略服务器所做的
文件功能并不理想,因为它们可能会在程序包更新后中断 .
理想的解决方案,恕我直言,应该是一个创建具有可继承
CAP_NET_BIND_SERVICE
集的shell的能力 .这是一个有点复杂的方法:
capsh
实用程序可以在Debian / Ubuntu发行版中的libcap2-bin包中找到 . 这是继续发生的事情:sg
将有效组ID更改为守护程序用户的ID . 这是必要的,因为capsh
保持GID不变,我们绝对不希望它 .设置位'keep capabilities on UID change' .
将UID更改为
$DAEMONUSER
删除所有上限(此时由于
--keep=1
,所有上限仍然存在),但可继承cap_net_bind_service
执行您的命令('--'是分隔符)
结果是具有指定用户和组以及
cap_net_bind_service
权限的进程 .例如,
ejabberd
启动脚本中的一行:您可以设置本地SSH隧道,例如,如果您希望端口80将您的应用程序绑定到3000:
这具有使用脚本服务器的优点,并且非常简单 .
或者修补内核并删除检查 .
(最后的选择,不推荐) .
另外两个简单的可能性
有一个旧的(不合时宜的)解决方案是“一个守护程序,它绑定在一个低端口并将控制权交给你的守护进程” . 它被称为inetd(或xinetd) . 缺点是:
你的守护进程需要在stdin / stdout上讨论(如果你没有't control the daemon -- if you don't有源 - 那么这可能是一个showstopper,虽然有些服务可能有一个inetd兼容性标志)
为每个连接分叉一个新的守护进程
这是链中的一个额外链接
优点:
可在任何旧UNIX上使用
一旦你的系统管理员设置了配置,你're good to go about your development (when you re-build your daemon, might you lose setcap capabilities? And then you'将不得不回到你的管理员"please sir...")
守护进程不必担心网络内容,只需要在stdin / stdout上进行讨论
可以根据请求配置为以非root用户身份执行守护程序
另一种选择:从特权端口到一些任意高编号端口的黑客代理(netcat甚至更强大的代理),你可以运行目标守护程序 . (Netcat显然不是 生产环境 解决方案,但是"just my dev box",对吧?) . 通过这种方式,您可以继续使用支持网络的服务器版本,只需要root / sudo启动代理(启动时),不会依赖复杂/可能脆弱的功能 .
我的“标准解决方法”使用socat作为用户空间重定向器:
请注意,这不会扩展,分叉是昂贵的,但这是socat的工作方式 .
Linux支持capabilities以支持比"this application is run as root"更细粒度的权限 . 其中一个功能是
CAP_NET_BIND_SERVICE
,它涉及绑定到特权端口(<1024) .不幸的是,我不知道如何利用它来运行非root用户同时仍然给它
CAP_NET_BIND_SERVICE
(可能使用setcap,但必然会有一个现有的解决方案) .我知道这是一个老问题,但现在最近(> = 4.3)内核终于有了一个很好的答案 - 环境功能 .
快速回答是获取最新(尚未发布)的libcap from git版本的副本并进行编译 . 将生成的
progs/capsh
二进制文件复制到某处(/usr/local/bin
是一个不错的选择) . 然后,以root身份启动您的程序按顺序,我们是
声明当我们切换用户时,我们希望保留当前的功能集
将用户和组切换为'your-service-user-name'
将
cap_net_bind_service
功能添加到继承和环境集分叉
bash -c 'your-command'
(因为capsh
自动在--
之后用参数启动bash)这里有很多内容 .
首先,我们以root身份运行,因此默认情况下,我们可以获得一整套功能 . 其中包括使用
setuid
和setgid
系统调用切换uid和gid的功能 . 但是,通常当程序执行此操作时,它会丢失其功能集 - 这样就可以使用setuid
删除root的旧方法仍然有效 .--keep=1
标志告诉capsh
发出prctl(PR_SET_KEEPCAPS)
系统调用,这会在更改用户时禁用删除功能 .capsh
实际更改了用户--user
标志,该标志运行setuid
和setgid
.我们需要解决的下一个问题是如何在我们的孩子们之后继续设置能力 . 功能系统始终具有'inherited'功能集,即" a set of capabilities preserved across an execve(2)" [capabilities(7)] . 虽然这听起来像是解决了我们的问题(只是将
cap_net_bind_service
功能设置为继承,对吗?),这实际上只适用于特权进程 - 而且我们的进程不再具有特权,因为我们已经更改了用户(使用--user
标志) .新的环境功能集解决了这个问题 - 它是"a set of capabilities that are preserved across an execve(2) of a program that is not privileged."通过将
cap_net_bind_service
放在环境集中,当capsh
exec是我们的服务器程序时,我们的程序将继承此功能并能够将侦听器绑定到低端口 .如果您有兴趣了解更多信息,manual page会详细解释这一点 . 通过
strace
运行capsh
也非常有用!TLDR: For "the answer" (as I see it), jump down to the >>TLDR<< part in this answer.
好吧,我已经弄明白了(这次真的是这个),这个问题的答案,我的这个答案也是一种道歉的方式,促使another answer(在这里和在推特上),我认为是"the best",但是之后尝试它,发现我错了 . 从错误的孩子那里学习:不要自己尝试过!
我再次回顾了这里的所有答案 . 我尝试了其中一些(并选择不尝试其他人,因为我根本不喜欢这些解决方案) . 我认为解决方案是使用
systemd
及其Capabilities=
和CapabilitiesBindingSet=
设置 . 在与此摔跤一段时间后,我发现这不是解决方案 because:Capabilities are intended to restrict root processes!
正如OP明智地指出的那样,最好避免这种情况(如果可能的话,为所有守护进程!) .
您不能在
systemd
单元文件中使用与User=
和Group=
相关的功能相关选项,因为在调用execev
(或任何函数)时,功能始终会重置 . 换句话说,当systemd
分叉并删除其权限时,功能将被重置 . 没有办法解决这个问题,内核中的所有绑定逻辑都是基于uid = 0的基础,而不是功能 . 这意味着Capabilities不太可能成为这个问题的正确答案(至少在很短的时间内) . 顺便提一下,正如其他人所提到的那样,setcap
不是解决方案 . 它并没有很好地处理脚本,并且无论何时文件发生变化都会重置这些脚本 .在我微薄的辩护中,我做了状态(在评论中我提到了(OP也提到了),是"2nd best solution" . :-P
>>TLDR<<
解决方案是将
systemd
与on-the-flyiptables
命令结合使用,如下所示(taken from DNSChain):在这里我们完成以下工作:
守护进程侦听5333,但是由于
iptables
,在53上成功接受了连接我们可以在单元文件中包含这些命令,因此我们可以避免让人头痛 .
systemd
为我们清除防火墙规则,确保在守护程序未运行时删除它们 .我们从未以root身份运行,并且我们无法进行权限提升(至少
systemd
声称),据说即使守护程序被泄露并设置uid=0
.不幸的是,
iptables
仍然是一个非常丑陋且难以使用的实用程序 . 例如,如果守护程序正在侦听eth0:0
而不是eth0
,则命令为slightly different .Update 2017:
使用authbind
比CAP_NET_BIND_SERVICE或自定义内核好多了 .
CAP_NET_BIND_SERVICE授予对二进制文件的信任,但不提供对每个端口访问的控制 .
Authbind授予用户/组信任并提供对每端口访问的控制,并支持IPv4和IPv6(最近添加了IPv6支持) .
安装:
apt-get install authbind
配置对相关端口的访问,例如所有用户和组的80和443:
authbind
执行命令(可选择指定
--deep
或其他参数,请参见手册页):例如
作为约书亚神话般的后续行动(=不推荐,除非你知道你做了什么)建议破解内核:
我第一次发布它here .
简单 . 使用普通或旧内核,您不需要 .
正如其他人所指出的,iptables可以转发一个端口 .
正如其他人所指出的,CAP_NET_BIND_SERVICE也可以完成这项工作 .
当然,如果从脚本启动程序,CAP_NET_BIND_SERVICE将失败,除非你在shell解释器上设置上限,这是没有意义的,你也可以以root身份运行你的服务......
例如对于Java,您必须将其应用于JAVA JVM
显然,这意味着任何Java程序都可以绑定系统端口 .
Dito为mono / .NET .
我是最好的想法 .
但由于这两种方法都是黑客攻击,为什么不通过解除限制来解除限制呢?
没有人说你必须运行一个普通的内核,所以你可以运行自己的内核 .
您只需下载最新内核的源代码(或您目前拥有的内核) . 然后,你去:
在那里你寻找这条线
并将其更改为
如果你不想有一个不安全的ssh情况,你可以改成它:#define PROT_SOCK 24
通常,我会使用您需要的最低设置,例如79用于http,或者在端口25上使用SMTP时为24 .
这已经是全部了 .
编译内核,然后安装它 .
重启 .
完成 - 这个愚蠢的限制是GONE,这也适用于脚本 .
以下是编译内核的方法:
https://help.ubuntu.com/community/Kernel/Compile
简而言之,如果你想保持安全,请使用iptables,如果你想确保这个限制再也不会困扰你,请编译内核 .
systemd是一个sysvinit替代品,可以选择启动具有特定功能的守护程序 . 选项Capabilities =,CapabilityBoundingSet = systemd.exec(5)联机帮助页 .
端口重定向对我们来说最有意义,但是我们遇到了一个问题,我们的应用程序将在本地解析一个也需要重新路由的URL; (这意味着你shindig) .
这也允许您在访问本地计算机上的URL时重定向 .
在启动时:
然后,您可以绑定到前进的端口 .
使用systemd,您只需稍微修改您的服务即可接受预先激活的套接字 .
您以后可以使用systemd socket activate .
不需要功能,iptables或其他技巧 .
这是简单的python http server这个例子的相关systemd文件的内容
档案
httpd-true.service
档案
httpd-true.socket
出于某种原因,没有人提到将sysctl net.ipv4.ip_unprivileged_port_start降低到所需的值 . 示例:我们需要将应用程序绑定到443端口 .
有人可能会说,存在潜在的安全问题:非特权用户现在可能绑定到其他特权端口(444-1024) . 但是你可以通过阻止其他端口轻松解决这个问题:
与其他方法比较 . 这个方法:
从某些角度来说(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设置)
没有't do LD_PRELOAD like authbind/privbind method, this could potentially affect performance, security, behavior (does it? haven'经过测试) . 在其余的authbind中是非常灵活和安全的方法 .
过度执行iptables REDIRECT / DNAT方法,因为它不需要地址转换,连接状态跟踪等 . 这在高负载系统上才会显着 .
根据具体情况,我会选择sysctl,CAP,authbind和iptables-redirect . 我们有很多选择,这很棒 .
还有'djb方式' . 您可以使用此方法以root身份在tcpserver下的任何端口上启动进程,然后它将在流程启动后立即将流程控制权交给您指定的用户 .
有关详细信息,请参阅:http://thedjbway.b0llix.net/daemontools/uidgid.html
使用privbind实用程序:它允许非特权应用程序绑定到保留端口 .
我尝试了iptables PREROUTING REDIRECT方法 . 在较旧的内核中,似乎是这种类型的规则wasn't supported for IPv6 . 但显然现在支持ip6tables v1.4.18和Linux内核v3.8 .
我还发现PREROUTING REDIRECT不适用于机器内启动的连接 . 要使用本地计算机中的连接,请同时添加OUTPUT规则 - 请参阅iptables port redirect not working for localhost . 例如 . 就像是:
我还发现PREROUTING REDIRECT also affects forwarded packets . 也就是说,如果机器也在接口之间转发数据包(例如,如果它连接到Internet目的地,并将它们重定向到机器 . 这不是我想要的 - 我只想重定向指向机器本身的连接 . 我发现我只能通过添加
-m addrtype --dst-type LOCAL
来影响发送到盒子的数据包 . 例如:另一种可能性是使用TCP端口转发 . 例如 . 使用
socat
:然而,该方法的一个缺点是,正在侦听端口8080的应用程序然后不知道传入连接的源地址(例如,用于记录或其他识别目的) .
2015年9月回答:
ip6tables现在支持IPV6 NAT:http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt
你需要内核3.7
证明: