是否可以调整内核参数以允许用户级程序绑定到端口80和443?

我问的原因是我认为允许特权进程打开套接字并监听它是愚蠢的。任何打开套接字并进行侦听的操作都是高风险的,并且高风险的应用程序不应以root用户身份运行。

我宁愿尝试弄清哪些非特权进程正在侦听端口80,而不是尝试删除具有超级用户权限的恶意软件。

评论

请参阅serverfault.com/questions/268099和stackoverflow.com/questions/413807。简短的答案是没有。

长答案是肯定的,所以短答案也应该是。

简短的答案是肯定的。

使用使用iptables重定向端口流量的noob答案。迄今为止最简单的解决方案,必要时可以轻松撤消。

#1 楼

我不确定这里的其他答案和评论是指什么。这很容易做到。有两个选项,两个选项都允许访问低编号的端口而不必将进程提升为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(手册页)工具正是为此目的而存在的。


使用您喜欢的软件包管理器安装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授予用户/组信任,并提供对每个端口访问的控制权,但是较旧的版本仅支持IPv4(因为我最初写此内容,因此发布了具有IPv6支持的较新版本)。

评论


是否真的需要rwx许可?

–马特
16-4-23在17:42

要还原选项1中的操作,是否可以使用+ eip的-p intead再次运行命令?

– artis3n
16年8月27日在15:49

请注意,使用setcap覆盖如果您授予可执行文件特权(例如,执行重建),那么它将失去其特权端口状态,因此您必须再次赋予其特权:|

–rogerdpack
16-10-27在22:25

我不得不摆弄的东西;我试图运行sysv服务,该服务运行使用ruby的ruby可执行文件。您需要授予特定于版本的ruby可执行文件的setcap权限,例如/usr/bin/ruby1.9.1

–克里斯蒂安·隆多
17年1月25日在19:31



我怀疑将byport文件更改为777是最好的主意。我见过的范围从500到744。我会坚持最适合您的限制。

–佩雷
17年5月9日在9:09



#2 楼

戴尔·哈格隆德(Dale Hagglund)就在现场。因此,我将讲一些相同的东西,但以不同的方式讲一些细节和例子。 in

在Unix和Linux领域中正确的做法是:


拥有一个小型,简单,易于审核的程序,该程序可以作为超级用户运行并绑定侦听套接字;
有另一个小的,简单,易于审核的程序,该程序会丢弃特权,由第一个程序产生;
在另一个单独的第三个程序中运行服务的内容在第二程序加载的非超级用户帐户和链下,期望仅继承套接字的打开文件描述符。

您对高风险的位置有错误的认识。高风险在于从网络中读取数据,并采取简单的操作(打开套接字,将其绑定到端口并调用listen())来处理未读取的内容。这是服务中进行实际通信的部分,因此风险很高。打开的部分bind()listen(),甚至(在某种程度上)accepts()的部分都不是高风险,可以在超级用户的支持下运行。它们不使用并根据网络上不受信任的陌生人的控制数据(在accept()情况下为源IP地址)。

有许多方法可以执行此操作。

inetd

正如Dale Hagglund所说,旧的“网络超级服务器” inetd就是这样做的。运行服务进程的帐户是inetd.conf中的列之一。它没有将侦听部分和放弃特权部分分为两个单独的程序,这些程序很小且易于审核,但它确实将主服务代码分离到了一个单独的程序中,该程序在服务过程中以打开的文件生成套接字的描述符。

审核的困难不是什么大问题,因为仅需审核一个程序即可。与最近的工具相比,exec()的主要问题不是审计太多,而是它没有提供简单的细粒度运行时服务控制。

UCSPI-TCP和daemontools

Daniel J. Bernstein的UCSPI-TCP和daemontools软件包旨在共同实现这一目标。也可以使用Bruce Guenter的等效daemontools-encore工具集。

用于打开套接字文件描述符并绑定到特权本地端口的程序是inetd,来自UCSPI-TCP。它同时执行tcpserverlisten()

accept()然后生成一个服务程序,该服务程序本身会失去root特权(因为所服务的协议涉及先以超级用户身份启动然后“登录”) (例如FTP或SSH守护程序)或tcpserver(这是一个自包含的小型且易于审核的程序,仅丢弃特权,然后将加载链式链接到适当的服务程序(因此,其中的任何部分都无法与超级用户一起运行)特权,例如setuidgid)。因此,例如,一个服务qmail-smtpd脚本(此脚本用于为dummyidentid提供空IDENT服务):

#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl


nosh

我的nosh软件包就是为此目的而设计的。像其他软件一样,它具有小的run实用程序。略有不同是它可以与setuidgid风格的“ LISTEN_FDS”服务以及UCSPI-TCP服务一起使用,因此传统的systemd程序被两个单独的程序取代:tcpservertcp-socket-listen

再次,单一用途的工具会相互产生和加载。该设计的一个有趣的怪癖是,可以在tcp-socket-accept之后甚至listen()之前放弃超级用户特权。这是一个用于accept()run脚本,确实确实做到了这一点:

#!/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 \
'


在超级用户的支持下运行的程序是与服务无关的小型链加载工具qmail-smtpdfdmoveclearenvenvdirsoftlimittcp-socket-listen。通过启动setuidgid的点,套接字已打开并绑定到sh端口,并且该进程不再具有超级用户特权。

s6,s6-networking和execline

Laurent Bercot的s6和s6网络软件包旨在共同实现这一目标。这些命令在结构上与smtp和UCSPI-TCP的命令非常相似。

daemontools脚本几乎相同,除了用run替换s6-tcpserver以及用tcpserver替换s6-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}


ipsvd

Gerrit Pape的ipsvd是另一个工具集,其运行方式与ucspi-tcp和s6-networking。这次使用的工具分别是setuidgidchpst,但是它们的作用相同,并且用于读取,处理和写入不受信任的客户端通过网络发送的事物的高风险代码仍在单独的程序中。

这是M. Pape在tcpsvd脚本中运行fnord的示例:

#!/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

systemd,新的服务监督和在某些Linux发行版中可以找到的init系统,旨在执行systemd可以执行的操作。但是,它不使用一组小型独立程序。不幸的是,一个人必须审核整个inetd

使用systemd可以创建配置文件来定义systemd侦听的套接字以及systemd启动的服务。服务“单元”文件的设置允许对服务进程进行大量控制,包括以哪个用户身份运行。

将该用户设置为非超级用户,systemd会执行所有操作打开套接字,将其绑定到端口并在进程#1中以超级用户身份调用systemd(如果需要,还需要listen())的工作,并且它产生的服务进程在没有超级用户特权的情况下运行。

评论


谢谢你的称赞。这是大量的具体建议。 +1。

–戴尔·哈格隆德
14-4-23在23:13



#3 楼

我有一个完全不同的方法。我想将80端口用于node.js服务器。因为为非sudo用户安装了Node.js,所以我无法执行此操作。我尝试使用符号链接,但对我而言不起作用。

然后我知道可以将连接从一个端口转发到另一个端口。因此,我在端口3000上启动了服务器,并设置了从端口80到端口3000的端口。

此链接提供了可用于执行此操作的实际命令。这是命令-


localhost / loopback

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


我使用了第二个命令,它对我有用。因此,我认为这是不允许用户进程直接访问较低端口,而是使用端口转发对其进行访问的中间立场。

评论


+1思考

–IpsRich
18-09-21在7:39

这是如此容易和简单。它应该是最佳答案。

–安德鲁·科斯特(Andrew Koster)
20 Mar 19 '20 at 2:41

#4 楼

最简单的解决方案:删除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)

现在,请注意安全性,因为所有用户都可以绑定所有端口!

评论


那很聪明。一个小巧的选择:该配置会打开80和443,但同时还会打开所有其他端口。可能不需要其他端口上的放宽权限。

– jww
19-09-13在13:36

不错的解决方案。我已将其用于IPv6,并且运行良好。这是我所做的:docs.google.com/document/d/e/…

–费尔南多
20-4-23在11:32

#5 楼

您的直觉是完全正确的:以root用户身份运行大型复杂程序是一个坏主意,因为它们的复杂性使它们难以信任。

但是,允许普通用户绑定特权端口也是一个坏主意,因为此类端口通常代表重要的系统服务。

解决此明显矛盾的标准方法是特权分离。基本思想是将程序分为两个(或多个)部分,每个部分在整个应用程序中定义明确,并通过简单的有限接口进行通信。

在示例中,您给出了,您希望将程序分为两部分。一个以root身份运行并打开并绑定到特权套接字,然后以某种方式将其移交给另一部分,该部分以常规用户身份运行。

这两种主要方法可实现这种分离。


以root身份启动的单个程序。它要做的第一件事就是以尽可能简单和有限的方式创建必要的套接字。然后,它放弃特权,也就是说,它将自身转换为常规用户模式进程,并执行所有其他工作。正确放弃特权是很棘手的,因此请花点时间研究正确的方法。
通过父进程创建的套接字对进行通信的一对程序。非特权驱动程序会接收初始参数,并且可能会进行一些基本参数验证。它通过socketpair()创建一对连接的套接字,然后分叉并执行另外两个将完成实际工作的程序,并通过套接字对进行通信。其中之一具有特权,将创建服务器套接字以及任何其他特权操作,而另一种将执行更复杂的应用程序,因此可信度较低。

[1] http://en.m.wikipedia.org/wiki/Privilege_separation

评论


您的建议不被视为最佳做法。您可能会看到inetd,它可以在特权套接字上侦听,然后将该套接字移交给非特权程序。

–戴尔·哈格隆德
2014年2月2日,9:59

#6 楼

可能最简单的方法是什么?
反向代理。 Nginx比iptables更简单(无论如何对我来说)。 Nginx还提供“ ssl终止”。
sudo apt install nginx
sudo service nginx start
# Verify it's working
curl http://localhost
# make certs
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/cert.key -out /etc/nginx/cert.crt
sudo nano /etc/nginx/conf.d/devserver.conf 

添加此内容
server {
    listen 80;
    return 301 https://$host$request_uri;
}

server {

    listen 443;
    server_name www.example.com;

    ssl_certificate           /etc/nginx/cert.crt;
    ssl_certificate_key       /etc/nginx/cert.key;

    ssl on;

    location / {

      proxy_set_header        Host $host;
      proxy_set_header        X-Real-IP $remote_addr;
      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header        X-Forwarded-Proto $scheme;

      proxy_pass          http://localhost:8080;
    }
}

重新启动服务器:
sudo service nginx restart

配置DNS:www的记录.example.com-> 127.0.0.1
# Test it out:
curl --insecure --verbose https://www.example.com