如何在Linux上以普通用户身份在端口80上运行服务器?


310

我已经用Google搜索了很长时间的解决方案,但是找不到答案。

我在Ubuntu Linux上并想在端口80上运行服务器,但是由于Ubuntu的安全性机制,出现以下错误:

java.net.BindException:权限被拒绝:80

我认为应该很简单,要么禁用此安全机制,以使端口80可供所有用户使用,要么为当前用户分配所需的特权以访问端口80。


3
在没有特权的另一个端口上运行服务器有什么问题?您正在考虑禁用安全机制之类的严厉措施,而没有提供至少一个非常严肃的理由来在该端口上运行服务器。服务器是否已硬编码绑定到端口80?如果是这样,请将其丢弃。
匿名


4
你不能 低于1024的端口具有特权,并且只有root可以打开它们上的侦听套接字。适当的操作是在打开权限后删除权限。
Falcon Momot 2013年

2
“匿名”-世界各地的用户都经过训练可以在某些港口寻找某些服务。在某些情况下,它是标准化的。例如,端口80上的HTTP和端口443上的HTTPS。这很难改变用户和世界标准。

1
@FalconMomot如果低于1024的端口具有特权,那么Apache服务器为什么不要求输入密码来获得端口80的root特权,它们怎么做呢?我认为发问者想知道如何以这种方式做事(无论从技术上讲它是否被用作root用户)。迪帕克·米塔尔(Deepak Mittal),如果我错了,请随时告诉我。
舒勒2015年

Answers:


354

简短答案:您不能。低于1024的端口只能由root用户打开。根据评论-很好,您可以使用CAP_NET_BIND_SERVICE,但是应用于Java bin的方法将使任何Java程序都可以使用此设置运行,这是不希望的,即使这样做不会带来安全风险。

答案很长:您可以将端口80上的连接重定向到可以以普通用户身份打开的其他端口。

以root身份运行:

# iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

由于回送设备(例如localhost)不使用预路由规则,因此,如果您需要使用localhost等,请也添加此规则(感谢@Francesco):

# iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 8080

注:上述方案不是非常适合于多用户系统,任何用户都可以打开端口8080(或者你决定使用任何其他高端口),因此截断交通。(归功于CesarB)。

编辑:根据评论问题-删除上述规则:

# iptables -t nat --line-numbers -n -L

这将输出类似:

Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination         
1    REDIRECT   tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:8080 redir ports 8088
2    REDIRECT   tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:80 redir ports 8080

您感兴趣的规则是nr。2,所以删除它:

# iptables -t nat -D PREROUTING 2

13
@Sunny:投票不是为西方最快的枪支,而是为了获得最佳反应。您到目前为止是最好的(我只提到了iptables;您实际上提供了完整的命令行)。唯一该矿拥有你不会是对其他用户也能够绑定到端口8080的警告
CesarB

3
请注意,这不适用于IPv6。
Emmanuel Bourg

3
任何人都可以解释一下以后如何删除此规则?运行它后,它可以工作,但显然不是“规则”,因为当我这样做时它不会显示sudo iptables --list。我知道iptables是做什么的,但是在此之前我从未真正使用过它。
编码器2012年

4
感谢您的回答...每当我重新启动Ubuntu时,该规则就会消失,我必须再次运行它。有没有办法永远保存它?
Coderji 2014年

4
:@Coderji:勾选“保存”部分社区文档中help.ubuntu.com/community/IptablesHowTo
阳光

79

使用authbind

如果启用Java的仅IPv4堆栈,它甚至可以与Java一起使用。我用:

authbind --deep $JAVA_HOME/bin/java -Djava.net.preferIPv4Stack=true …

4
如果服务器是Tomcat,则可以通过AUTHBIND=yes在/ etc / default / tomcat6中进行设置来自动使用authbind
Emmanuel Bourg'4

我无法使用默认的Java软件包在Ubuntu服务器上使用它
Ashley

我也没有,任何已知的解决方案吗?
2012年

10
请注意,您必须配置authbind为实际允许这种情况发生。在手册页中:“已测试/ etc / authbind / byport / port。如果根据access(2),调用方用户可以访问此文件以执行该文件,则将授权绑定到该端口。” ,例如80端口sudo touch /etc/authbind/byport/80 ; sudo chmod 777 /etc/authbind/byport/80。的初始安装authbind通常没有任何预先配置的授权。
杰森C

4
哦,nonononononono,永远不要chmod 777什么!
约翰尼斯

57

如果您的系统支持,则可以使用功能。查看人员功能,您需要的是CAP_NET_BIND_SERVICE。

在较新的Debian / Ubuntu上,您可以运行:

sudo apt-get install libcap2-bin 
sudo setcap 'cap_net_bind_service=+ep' /path/to/program

这适用于nodejs:setcap'cap_net_bind_service = + ep'/ usr / bin / nodejs
JasonS 2015年

5
这个。我想知道为什么这个答案没有更多支持。比iptables选项imho容易得多。
Dominik R

5
这是正确且最有效的答案,所有其他答案都会导致性能下降或只是轻率/不安全。
OneOfOne 2013年

41

另一个解决方案是使您的应用程序setuid可以与端口80绑定。以root身份执行以下操作

chown root ./myapp
chmod +S ./myapp

请记住,除非绝对正确,否则这样做将使您面临潜在的安全漏洞,因为您的应用程序将与网络进行通信,并且将以完全根特权运行。如果采用此解决方案,则应查看Apache或Lighttpd或类似内容的源代码,它们在其中使用root特权打开端口,但随后立即放弃这些priv并“成为”低特权用户,以便劫机者无法接管您的整个计算机。

更新:从该问题中可以看出,自2.6.24以来的Linux内核似乎具有一项新功能,可让您将可执行文件(当然,不是脚本)标记为具有“ CAP_NET_BIND_SERVICE”功能。如果安装了debian软件包“ libcap2-bin”,则可以通过发出以下命令来完成

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

6
除非应用程序知道如何删除特权,否则这与以root用户身份运行是相同的。
CesarB

14
这很危险。这意味着任何请求都将以root用户身份运行。这是有原因的,即使apache也以root用户身份开始绑定,然后将特权放弃给另一个用户。

9
Paul:iptables并不是那么危险,因为即使该应用程序被盗用,它也不会使系统遭受攻击,至少没有root特权。以root用户身份运行该应用程序是另一回事。

1
正如CesarB所说,iptables的危险仅在于它是一个多用户系统,因为任何人都可以绑定到8080,并拦截流量。

1
大声笑,爱被接受的答案是如何产生-15
Evan Teran

40

我只是在前面使用Nginx。它也可以在本地主机上运行。

  • apt-get install nginx

.. 要么 ..

  • pkg_add -r nginx

..或适合您的操作系统的内容。

如果在localhost上运行,则在nginx.conf中需要的全部是:

服务器{
        听80;
        server_name some.domain.org;
        位置 / {
            proxy_set_header主机$ host;
            proxy_set_header X-Real-IP $ remote_addr;
            proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:8081;
        }
}

我真的很喜欢这个解决方案。
thomasfedb

1
这是JFrog本身建议的解决方案之一(jfrog.com/confluence/display/RTF/nginx
stolsvik 2014年

36

Sunny和CesarB提出的方法:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

可以正常工作,但是有一个小缺点-它不会阻止用户直接连接到8080端口而不是80端口。

当这可能是一个问题时,请考虑以下情形。

假设我们有一个服务器,该服务器在端口8080上接受HTTP连接,并在端口8181上接受HTTPS连接。

我们使用iptables建立以下重定向:

80  ---> 8080
443 ---> 8181

现在,假设我们的服务器决定将用户从HTTP页面重定向到HTTPS页面。除非我们仔细重写响应,否则它将重定向到https://host:8181/。至此,我们被搞砸了:

  • 一些用户会为该https://host:8181/URL添加书签,而我们需要维护该URL以避免破坏其书签。
  • 其他用户将无法连接,因为他们的代理服务器不支持非标准SSL端口。

我使用以下方法:

iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -p tcp --dport 443 -j MARK --set-mark 1
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8181
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -m mark --mark 1 -j ACCEPT
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8181 -m mark --mark 1 -j ACCEPT

结合INPUT链上的默认REJECT规则,此方法可防止用户直接连接到端口8080、8181


1
很好,但是为什么不将守护程序绑定到localhost:8080而不是0.0.0.0:8080呢?我想这样做,但是我需要iptables。
阿马拉

它的工作原理,真的很酷。
xtian

29

传统上,在Unix上,只有root可以绑定到低端口(<1024)。

解决此问题的最简单方法是在端口(例如8080)上运行服务器,并使用简单的iptables规则将连接从端口80转发到端口8080。请注意,这样做会失去对服务器的额外保护。低端口;您计算机上的任何用户都可以绑定到端口8080。


23

如果您的系统支持,则可以使用功能。瞧man capabilities,你需要一个CAP_NET_BIND_SERVICE。不,我从来没有亲自使用过它们,也不知道它们是否真的起作用:-)


1
它们确实起作用,我以普通用户身份使用CAP_NET_RAW运行tcpdump之类的工具。
贾斯汀2010年

12

在应用程序服务器前使用反向代理(nginx,apache + mod_proxy)或缓存反向代理(Squid,Varnish)!

使用反向代理,您可以实现很多有趣的事情,例如:

  • 负载均衡
  • 在用户收到奇特的错误页面的情况下重新启动应用程序服务器
  • 利用缓存加快处理速度
  • 通常使用反向代理而不是应用程序服务器进行的细粒度设置

6

您可以使用redir程序:

sudo redir --lport=80 --laddr=192.168.0.101 --cport 9990 --caddr=127.0.0.1

4

使用sudo。

配置sudo,以便普通用户可以运行适当的命令:

/etc/init.d/httpd start

要么

apachectl stop

要么

/usr/local/apache2/apachectl restart

要么

/myapp/myappbin start

(或者您用于启动/停止特定Web服务器/应用程序的其他命令/脚本)


8
这有同样的问题-以root身份运行服务是一种不良做法。
凯文·潘科

4

Sunny的答案是正确的,但是您可能会遇到其他问题,因为回送接口不使用PREROUTING表,

因此要添加的iptables规则有两个:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 8080


2

一种解决方案是使用iptables对端口80的数据包执行PAT。例如,您可以使用iptables将数据包路由到本地端口8080。确保将传出数据包调整回端口80。

以我的经验,由于安全问题,Linux的细粒度权限功能没有编译到标准内核中。


2

如果尝试这样做,以便用户运行的命令可以使用端口80,则唯一的解决方案是iptables技巧或将可执行的setuid-to-root设置。

像Apache这样的方式(它绑定到端口80,但以除root以外的身份运行)的方式是:以root身份运行,绑定到端口,然后在端口之后将进程的所有权更改为非特权用户设置好了。如果您正在编写的应用程序可以由root用户运行,则可以在设置端口后将其更改为非私有用户。但是,如果这仅是让普通用户从命令行运行,则必须使用其他解决方案之一。


2

当我不想以root身份运行各种Web服务应用程序(Python脚本,tomcat引擎等)时,通常会在它们前面配置一个apache Web服务器。Apache监听端口80,tomcat监听8080。

在apache:s的配置中:

ProxyPass /webapp http://localhost:8080/webapp
ProxyPassReverse /webapp http://localhost:8080/webapp

有关更多信息,请参见mod-proxy文档:http : //httpd.apache.org/docs/2.2/mod/mod_proxy.html


1
该解决方案的缺点是Tomcat日志中的传入IP丢失。
Emmanuel Bourg

2

我认为最好的解决方案是引导您的应用程序,一旦其端口绑定,它应该通过切换到另一个用户来放弃特权。


1

某些主机系统不允许使用NAT模块,在这种情况下,“ iptables”不能解决问题。

xinetd怎么样?

就我而言(ubuntu 10.04)

# apt-get install xinetd
# touch /etc/xinetd.d/my_redirect
# vim /etc/xinetd.d/my_redirect

粘贴配置:

service my_redirector_80
{
 disable = no
 socket_type = stream
 protocol = tcp
 user = root
 wait = no
 port = 80
 redirect = localhost 8080
 type = UNLISTED
}

然后:

# service xinetd restart

http://docs.codehaus.org/display/JETTY/port80可以更好地说明。


1
这基本上等效于将反向代理放在服务器前面,但是与了解HTTP协议并可以添加有用的标头(例如X-Forwarded-For)的专用解决方案(例如HAproxy,Pound或Varnish)相比,效率较低。
Emmanuel Bourg

-1

顺便说一句,FreeBSD和的Solaris(还有人记得一个?)让你这样做没有特权升级(绑定到低端口)(即使用程序切换到root)。既然您指定了Linux,我只是将其发布给其他可能会发现此问题的人。


-1

死灵法师。

简单。使用普通或旧内核,则不需要。
正如其他人指出的那样,iptables可以转发端口。
正如其他人指出的那样,CAP_NET_BIND_SERVICE也可以完成这项工作。
当然,如果您从脚本启动程序,CAP_NET_BIND_SERVICE将会失败,除非您在shell解释器上设置了上限,这是没有意义的,您也可以像root一样运行服务...
例如,对于Java,您必须应用它到Java JVM

sudo /sbin/setcap 'cap_net_bind_service=ep' /usr/lib/jvm/java-8-openjdk/jre/bin/java

显然,这意味着任何Java程序都可以绑定系统端口。
Dito for mono / .NET。

我也很确定xinetd并不是最好的主意。
但是由于这两种方法都是骇客,为什么不通过解除限制来解除限制呢?
没有人说您必须运行普通的内核,因此您可以运行自己的内核。

您只需下载最新内核的源代码(或当前使用的内核)。之后,您将转到:

/usr/src/linux-<version_number>/include/net/sock.h:

在那里你寻找这条线

/* Sockets 0-1023 can't be bound to unless you are superuser */
#define PROT_SOCK       1024

并将其更改为

#define PROT_SOCK 0

如果您不想出现不安全的ssh情况,请将其更改为:#define PROT_SOCK 24

通常,我将使用所需的最低设置,例如,http设置为79,端口25上使用SMTP时设置为24。

已经足够了。
编译内核,然后安装它。
重启。
完成-该愚蠢的限制已消失,它也适用于脚本。

这是编译内核的方法:

https://help.ubuntu.com/community/Kernel/Compile

# You can get the kernel-source via package linux-source, no manual download required
apt-get install linux-source fakeroot

mkdir ~/src
cd ~/src
tar xjvf /usr/src/linux-source-<version>.tar.bz2
cd linux-source-<version>

# Apply the changes to PROT_SOCK define in /include/net/sock.h

# Copy the kernel config file you are currently using
cp -vi /boot/config-`uname -r` .config

# Install ncurses libary, if you want to run menuconfig
apt-get install libncurses5 libncurses5-dev

# Run menuconfig (optional)
make menuconfig

# Define the number of threads you wanna use when compiling (should be <number CPU cores> - 1), e.g. for quad-core
export CONCURRENCY_LEVEL=3
# Now compile the custom kernel
fakeroot make-kpkg --initrd --append-to-version=custom kernel-image kernel-headers

# And wait a long long time

cd ..

简而言之,如果要保持安全性,请使用iptables;如果要确保此限制永远不会再困扰您,请编译内核。


有人可以解释一下赞成票吗?内核编译不起作用?还是只是因为之后,您不能再从普通存储库中进行内核升级了?
困惑

我尝试了此解决方案(sudo setcap cap_net_bind_service=+ep /path-to-java/java),但java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory如果java设置了上限,我总能得到。另请参见unix.stackexchange.com/a/16670/55508
Daniel Alder

1
加入解决了以前的错误<javahome>/lib/amd64/jli/,以ld.so.conf和运行sudo ldconfig
丹尼尔·阿尔德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.