在我的开发盒上有这种限制是非常令人讨厌的,因为除了我之外再也没有其他用户了。

我知道一些标准的变通办法,但没有一个能完全满足我的要求:

authbind (Debian测试中的版本,1.0,仅支持IPv4) 使用iptables REDIRECT目标将低端口重定向到高端口(iptables的IPv6版本ip6tables尚未实现“nat”表) sudo(作为根是我试图避免的) SELinux(或类似的)。(这只是我的开发框,我不想引入很多额外的复杂性。)

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

编辑:在某些情况下,您可以使用功能来做到这一点。


当前回答

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

快速的答案是从git中获取libcap的最新(尚未发布的)版本的副本并编译它。将生成的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'

按顺序,我们是

声明当切换用户时,我们希望保持当前的功能集 切换用户和组到“your-service-user-name” 将cap_net_bind_service功能添加到继承的&环境集 派生bash -c 'your-command'(因为capsh会自动用——后面的参数启动bash)

这里隐藏着很多秘密。

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

新的环境能力集解决了这个问题——它是“在一个没有特权的程序的执行中保存的一组能力。”通过将cap_net_bind_service放在环境集中,当capsh exec执行我们的服务器程序时,我们的程序将继承此功能,并能够将侦听器绑定到低端口。

如果您有兴趣了解更多信息,功能手册页详细解释了这一点。通过strace运行capsh也是非常有用的!

其他回答

你可以设置一个本地SSH隧道,例如,如果你想要端口80击中你的应用绑定到3000:

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

这样做的优点是可以使用脚本服务器,而且非常简单。

好的,感谢那些指出系统和CAP_NET_BIND_SERVICE功能的人。如果您有一个最新的内核,确实可以使用它来以非root身份启动服务,但绑定低端口。简单的回答是:

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

然后在任何程序执行之后,它都将具有CAP_NET_BIND_SERVICE能力。Setcap在debian包libcap2-bin中。

现在要注意的是:

You will need at least a 2.6.24 kernel This won't work if your file is a script. (i.e. 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. 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.

资源:

能力(7)手册页。如果您打算在生产环境中使用功能,请仔细阅读这篇文章。这里详细介绍了如何在exec()调用之间继承功能的一些非常棘手的细节。 Setcap手册页 “在GNU/Linux上绑定1024以下的端口,没有根”:第一次让我想到setcap的文档。

注意:RHEL首次在v6中添加了这个功能。

我尝试了iptables PREROUTING REDIRECT方法。在旧的内核中,IPv6似乎不支持这种类型的规则。但显然,现在ip6tables v1.4.18和Linux内核v3.8支持它。

我还发现PREROUTING REDIRECT对机器内启动的连接不起作用。要处理来自本地机器的连接,还需要添加一个OUTPUT规则-参见iptables端口重定向不适用于本地主机。例如:

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

另一种可能是使用TCP端口转发。例如使用socat:

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

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

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

您可以进行端口重定向。这就是我为运行在Linux机器上的Silverlight策略服务器所做的工作

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