jww*_*jww 167 linux privileges kernel sockets
是否可以调整内核参数以允许用户程序绑定到端口 80 和 443?
我问的原因是我认为允许特权进程打开套接字并侦听是愚蠢的。任何打开套接字并侦听的东西都是高风险的,高风险的应用程序不应以 root 身份运行。
我更愿意尝试找出哪些非特权进程正在侦听端口 80,而不是尝试删除以 root 权限潜入的恶意软件。
Jas*_*n C 237
我不确定这里的其他答案和评论指的是什么。这很容易实现。有两个选项,它们都允许访问低编号端口,而无需将进程提升为 root:
选项 1:CAP_NET_BIND_SERVICE
用于授予对进程的低编号端口访问权限:
有了这个,您可以通过以下setcap
命令授予对特定二进制文件的永久访问权限以绑定到低编号端口:
sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary
Run Code Online (Sandbox Code Playgroud)
有关 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
Run Code Online (Sandbox Code Playgroud)现在通过authbind
(可选地指定--deep
或其他参数,请参阅手册页)执行您的命令:
authbind --deep /path/to/binary command line args
Run Code Online (Sandbox Code Playgroud)
例如
authbind --deep java -jar SomeServer.jar
Run Code Online (Sandbox Code Playgroud)以上两者都有优点和缺点。选项 1 授予对二进制文件的信任,但不提供对每个端口访问的控制。选项 2 授予用户/组信任并提供对每个端口访问的控制,但旧版本仅支持 IPv4(因为我最初编写此内容,因此发布了支持 IPv6 的新版本)。
Jde*_*eBP 34
戴尔·哈格伦德 (Dale Hagglund) 就在现场。所以我只是要说同样的事情,但以不同的方式,以及一些细节和例子。☺
在 Unix 和 Linux 世界中正确的做法是:
您对高风险的位置有错误的认识。高风险在于从网络读取并根据读取的内容采取行动,而不是打开套接字、将其绑定到端口和调用listen()
. 进行实际通信的服务的一部分是高风险的。打开 ,bind()
和的部分,listen()
甚至(在一定程度上) , 的部分accepts()
不是高风险的部分,可以在超级用户的支持下运行。他们不使用和操作(在这种情况下源 IP 地址除外accept()
)由网络上不受信任的陌生人控制的数据。
有很多方法可以做到这一点。
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
脚本将是(此脚本用于dummyidentd以提供空 IDENT 服务):
#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl
Run Code Online (Sandbox Code Playgroud)
我的 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 \
'
Run Code Online (Sandbox Code Playgroud)
该超级用户的支持下运行的程序是小服务无关的链装载工具fdmove
,clearenv
,envdir
,softlimit
,tcp-socket-listen
,和setuidgid
。到sh
启动时,套接字已打开并绑定到smtp
端口,进程不再具有超级用户权限。
Laurent Bercot 的s6和s6-networking软件包旨在结合使用此功能。这些命令在结构上daemontools
与 UCSPI-TCP的命令非常相似。
run
脚本大致相同,除了替换为s6-tcpserver
fortcpserver
和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}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
systemd
systemd
,可以在某些 Linux 发行版中找到的新服务监督和初始化系统,旨在做inetd
可以做的事情。但是,它不使用一套小型自包含程序。systemd
不幸的是,必须全面审计。
用systemd
one 创建配置文件来定义一个systemd
侦听的套接字和一个systemd
启动的服务。服务“单元”文件具有允许对服务进程进行大量控制的设置,包括它以何种用户身份运行。
将该用户设置为非超级用户,systemd
完成打开套接字、将其绑定到端口、以超级用户身份调用listen()
(以及,如果需要,accept()
)进程 #1 以及它的服务进程的所有工作spawns 在没有超级用户权限的情况下运行。
noo*_*oob 34
我有一个相当不同的方法。我想将端口 80 用于 node.js 服务器。因为 Node.js 是为非 sudo 用户安装的,所以我无法做到。我尝试使用符号链接,但它对我不起作用。
然后我知道我可以将连接从一个端口转发到另一个端口。所以我在端口 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
我使用了第二个命令,它对我有用。所以我认为这是一个中间立场,不允许用户进程直接访问较低的端口,而是使用端口转发让他们访问。
小智 14
最简单的解决方案:删除 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
Run Code Online (Sandbox Code Playgroud)
(适用于具有非 root 帐户的 VirtualBox)
现在,请注意安全性,因为所有用户都可以绑定所有端口!
您的直觉是完全正确的:以 root 身份运行大型复杂程序是一个坏主意,因为它们的复杂性使它们难以信任。
但是,允许普通用户绑定到特权端口也是一个坏主意,因为这些端口通常代表重要的系统服务。
解决这一明显矛盾的标准方法是特权分离。基本思想是将您的程序分成两个(或更多)部分,每个部分执行整个应用程序的一个明确定义的部分,并通过简单的有限接口进行通信。
在您给出的示例中,您希望将程序分成两部分。一个以 root 身份运行并打开并绑定到特权套接字,然后以某种方式将其交给以普通用户身份运行的另一部分。
这两种主要方式实现了这种分离。
以 root 身份启动的单个程序。它所做的第一件事就是以尽可能简单和有限的方式创建必要的套接字。然后,它放弃特权,即将自身转换为常规用户模式进程,并执行所有其他工作。正确删除权限很棘手,所以请花时间研究正确的方法。
通过父进程创建的套接字对进行通信的一对程序。非特权驱动程序接收初始参数,并可能进行一些基本的参数验证。它通过创建一对连接的套接字socketpair()
,然后派生并执行另外两个将完成实际工作的程序,并通过套接字对进行通信。其中一个是有特权的,将创建服务器套接字和任何其他特权操作,另一个将执行更复杂且因此可信度较低的应用程序执行。
归档时间: |
|
查看次数: |
198818 次 |
最近记录: |