Answers:
我不确定这里的其他答案和评论是指什么。这很容易做到。有两个选项,这两个选项都允许访问低编号的端口,而不必将进程提升为root:
选项1:用于CAP_NET_BIND_SERVICE
向进程授予低号端口访问权限:
使用此命令,您可以通过以下setcap
命令授予对特定二进制文件的永久访问权,以绑定到低号端口:
sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary
有关e / i / p部分的更多详细信息,请参见cap_from_text
。
完成此操作后,/path/to/binary
将能够绑定到低编号的端口。请注意,您必须setcap
在二进制文件本身而不是符号链接上使用。
选项2:authbind
用于授予一次性访问权限,并具有更好的用户/组/端口控制:
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
上面两个都有优点和缺点。选项1将信任授予二进制文件,但不提供对每个端口访问的控制。选项2将信任授予用户/组,并提供对每个端口访问的控制,但是AFAIK仅支持IPv4。
rwx
许可吗?
-p
intead of 重新运行命令+eip
?
setcap
于版本的ruby可执行文件的权限,例如/usr/bin/ruby1.9.1
chmod
将777 byport
文件归档是最好的主意。我看过的范围从500
到744
。我会坚持最适合您的限制。
戴尔·哈格隆德(Dale Hagglund)就在现场。因此,我只是用一些细节和示例,以不同的方式讲同样的话。☺
在Unix和Linux世界中,正确的做法是:
您对高风险的位置有错误的认识。高风险在于从网络读取数据并采取行动,而不是通过打开套接字,将其绑定到端口并进行调用这样简单的操作来完成listen()
。这是服务中进行实际通信的一部分,因此风险很高。打开,bind()
和的部分,listen()
甚至(在一定程度上)打开的部分accepts()
都不是高风险,可以在超级用户的支持下运行。它们不会使用和处理accept()
网络中不受信任的陌生人控制的数据(在这种情况下,源IP地址除外)。
有很多方法可以做到这一点。
inetd
正如Dale Hagglund所说,旧的“网络超级服务器”就是inetd
这样做的。用于运行服务进程的帐户是中的列之一inetd.conf
。它没有将侦听部分和放弃特权部分分成两个单独的程序,这些程序很小且易于审核,但是它确实将主服务代码分离到了一个单独的程序中,并exec()
在一个带有打开文件描述符的服务过程中进行了编辑。用于插座。
审核的困难不是什么大问题,因为仅需审核一个程序即可。 inetd
的主要问题不是审核太多,而是与最新的工具相比,它没有提供简单的细粒度运行时服务控制。
Daniel J. Bernstein的UCSPI-TCP和daemontools软件包旨在共同实现这一目标。也可以使用Bruce Guenter的等效daemontools-encore工具集。
tcpserver
从UCSPI-TCP 打开套接字文件描述符并绑定到特权本地端口的程序是。它同时执行listen()
和accept()
。
tcpserver
然后生成一个服务程序,该服务程序本身会失去root特权(因为所服务的协议涉及先以超级用户身份开始,然后“登录”(例如,FTP或SSH守护程序就是这种情况)),或者setuidgid
是自包含的小型且易于审核的程序,该程序仅丢弃特权,然后将加载链正确地链接到服务程序(因此,其中的任何部分都不会以超级用户特权运行,例如qmail-smtpd
)。
run
因此,将以一个服务脚本为例(此脚本用于提供空IDENT服务的dummyidentd):
#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl
我的nosh软件包就是为此目的而设计的。setuidgid
和其他程序一样,它的实用程序很小。略有不同是它可用于systemd
-style“ LISTEN_FDS”服务以及UCSPI-TCP服务,因此传统tcpserver
程序被两个单独的程序代替:tcp-socket-listen
和tcp-socket-accept
。
同样,单一用途的实用程序会相互产生和加载。该设计的一个有趣的怪癖是,可以在超级用户特权之后listen()
甚至之前放弃超级用户特权accept()
。这里的run
脚本qmail-smtpd
确实可以做到这一点:
#!/bin/nosh
fdmove -c 2 1
clearenv --keep-path --keep-locale
envdir env/
softlimit -m 70000000
tcp-socket-listen --combine4and6 --backlog 2 ::0 smtp
setuidgid qmaild
sh -c 'exec \
tcp-socket-accept -v -l "${LOCAL:-0}" -c "${MAXSMTPD:-1}" \
ucspi-socket-rules-check \
qmail-smtpd \
'
该超级用户的支持下运行的程序是小服务无关的链装载工具fdmove
,clearenv
,envdir
,softlimit
,tcp-socket-listen
,和setuidgid
。到sh
启动时,套接字已打开并绑定到smtp
端口,并且该进程不再具有超级用户特权。
Laurent Bercot的s6和s6网络软件包旨在共同实现这一目标。这些命令在结构上与daemontools
和UCSPI-TCP 的命令非常相似。
run
除了替换s6-tcpserver
for tcpserver
和s6-setuidgid
for 之外,脚本将几乎相同setuidgid
。但是,也可能会选择同时使用M. Bercot的execline工具集。
这是一个FTP服务的示例,它是对Wayne Marshall的原始服务进行了轻微修改的,它使用execline,s6,s6-networking和publicfile的FTP服务器程序:
#!/command/execlineb -PW
multisubstitute {
define CONLIMIT 41
define FTP_ARCHIVE "/var/public/ftp"
}
fdmove -c 2 1
s6-envuidgid pubftp
s6-softlimit -o25 -d250000
s6-tcpserver -vDRH -l0 -b50 -c ${CONLIMIT} -B '220 Features: a p .' 0 21
ftpd ${FTP_ARCHIVE}
Gerrit Pape的ipsvd是另一个与ucspi-tcp和s6-networking相同的工具集。这些工具是chpst
和tcpsvd
这段时间,但他们做同样的事情,而且做阅读,处理,以及由不可信的客户端通过网络发送的东西写在高风险的代码仍然是在一个单独的程序。
#!/bin/sh
exec 2>&1
cd /public/10.0.5.4
exec \
chpst -m300000 -Uwwwuser \
tcpsvd -v 10.0.5.4 443 sslio -v -unobody -//etc/fnord/jail -C./cert.pem \
fnord
systemd
systemd
,可以在某些Linux发行版中找到的新服务监督和初始化系统,旨在做inetd
可以做的事情。但是,它不使用一组小型独立程序。systemd
不幸的是,必须对整个系统进行审计。
用systemd
一个创建配置文件以定义一个systemd
侦听的套接字和一个systemd
启动的服务。服务“单元”文件的设置允许对服务过程进行大量控制,包括以哪个用户身份运行。
在将该用户设置为非超级用户的情况下,systemd
完成打开套接字,将其绑定到端口并在进程#1中以超级用户身份调用listen()
(以及在需要时调用accept()
)的所有工作,以及该服务的处理过程生成运行时没有超级用户权限。
我有一个完全不同的方法。我想将80端口用于node.js服务器。因为为非sudo用户安装了Node.js,所以我无法执行此操作。我尝试使用符号链接,但对我而言不起作用。
然后我知道我可以将连接从一个端口转发到另一个端口。因此,我在端口3000上启动了服务器,并设置了一个从端口80转发至端口3000的端口。
该链接提供了可用于执行此操作的实际命令。这是命令-
本地主机/环回
sudo iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 3000
外部
sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000
我使用了第二个命令,它对我有用。因此,我认为这是不允许用户进程直接访问较低端口,而是使用端口转发对其进行访问的中间立场。
您的直觉是完全正确的:以root用户身份运行大型复杂程序是一个坏主意,因为它们的复杂性使它们难以信任。
但是,允许普通用户绑定到特权端口也是一个坏主意,因为此类端口通常代表重要的系统服务。
解决此明显矛盾的标准方法是特权分离。基本思想是将程序分为两个(或多个)部分,每个部分在整个应用程序中定义明确,并通过简单的有限接口进行通信。
在您给出的示例中,您希望将程序分为两部分。一个以root用户身份运行并打开并绑定到特权套接字,然后以某种方式将其交给另一部分,该部分以普通用户身份运行。
这两种主要方法可以实现这种分离。
以root身份启动的单个程序。它所做的第一件事就是以尽可能简单和有限的方式创建必要的套接字。然后,它放弃特权,也就是说,它将自身转换为常规用户模式进程,并执行所有其他工作。正确放弃特权是很棘手的,因此请花点时间研究正确的方法。
通过父进程创建的套接字对进行通信的一对程序。非特权驱动程序接收初始参数,并可能执行一些基本参数验证。它通过socketpair()创建一对连接的套接字,然后派生并执行另外两个程序,它们将完成实际工作,并通过套接字对进行通信。其中之一具有特权,将创建服务器套接字以及任何其他特权操作,而另一种将执行更复杂的应用程序,因此可信度较低。
最简单的解决方案:删除Linux上的所有特权端口
适用于ubuntu / debian:
#save configuration permanently
echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf
#apply conf
sysctl --system
(适用于具有非root帐户的VirtualBox)
现在,请注意安全性,因为所有用户都可以绑定所有端口!