当我的开发盒上没有除了我之外的任何用户时,我的开发盒受到这种限制是非常烦人的。

我知道 标准解决方法, ,但他们都没有完全按照我想要的方式做:

  1. 授权绑定 (Debian测试中的版本为1.0,仅支持IPv4)
  2. 使用 iptables REDIRECT 目标将低端口重定向到高端口 (ip6tables(iptables 的 IPv6 版本)尚未实现“nat”表)
  3. sudo (我试图避免以 root 身份运行)
  4. SELinux(或类似的)。(这只是我的开发盒,我不想引入很多额外的复杂性。)

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

编辑:在某些情况下,您可以 使用能力 去做这个。

有帮助吗?

解决方案

好的,感谢指出能力系统和 CAP_NET_BIND_SERVICE 能力。如果您有最新的内核,确实可以使用它以非 root 身份启动服务但绑定低端口。简短的回答是你这样做:

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

然后随时 program 之后执行它将有 CAP_NET_BIND_SERVICE 能力。 setcap 位于 debian 包中 libcap2-bin.

现在注意事项:

  1. 您至少需要 2.6.24 内核
  2. 如果您的文件是脚本,这将不起作用。(即使用#!行启动解释器)。在这种情况下,据我所知,您必须将该功能应用于解释器可执行文件本身,这当然是一个安全噩梦,因为任何使用该解释器的程序都将具有该功能。我无法找到任何干净、简单的方法来解决这个问题。
  3. Linux 将禁用任何 LD_LIBRARY_PATH program 具有提升的特权,例如 setcap 或者 suid. 。所以如果你的 program 使用自己的 .../lib/, ,您可能需要考虑其他选项,例如端口转发。

资源:

笔记: RHEL 首先在 v6 中添加了这个.

其他提示

标准方法是使它们成为“setuid”。所以他们以root用户身份启动,然后他们一旦绑定到端口但在他们开始接受连接之前就抛弃root权限。您可以在Apache和INN的源代码中看到这方面的好例子。我被告知Lighttpd是另一个很好的例子。

另一个例子是Postfix,它使用通过管道进行通信的多个守护进程,并且只有一个或两个(除了接受或发出字节之外几乎没有)以root身份运行,其余的以较低权限运行。

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

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

您可以设置本地SSH隧道,例如,如果您希望端口80命中您的应用程序绑定到3000:

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

这具有使用脚本服务器的优点,并且非常简单。

或修补你的内核并删除支票。

(最后的选择,不推荐)。

net / ipv4 / af_inet.c 中,删除两行

      if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
              goto out;

并且内核将不再检查特权端口。

文件功能并不理想,因为它们可能会在软件包更新后中断。

恕我直言,理想的解决方案应该是能够创建具有可继承性的 shell CAP_NET_BIND_SERVICE 放。

这是一种有点复杂的方法:

sg $DAEMONUSER "capsh --keep=1 --uid=`id -u $DAEMONUSER` \
     --caps='cap_net_bind_service+pei' -- \
     YOUR_COMMAND_GOES_HERE"

capsh 实用程序可以在 Debian/Ubuntu 发行版的 libcap2-bin 软件包中找到。事情是这样的:

  • sg 将有效组 ID 更改为守护程序用户的 ID。这是必要的,因为 capsh 让 GID 保持不变,我们绝对不想要它。
  • 设置“在 UID 更改时保持功能”位。
  • 将 UID 更改为 $DAEMONUSER
  • 删除所有大写字母(此时所有大写字母仍然存在,因为 --keep=1),可继承的除外 cap_net_bind_service
  • 执行您的命令(“--”是分隔符)

结果是具有指定用户和组的进程,并且 cap_net_bind_service 特权。

举个例子,一行来自 ejabberd 启动脚本:

sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"

另外两种简单的可能性:

对于“绑定在低端口上并将控制权交给您的守护进程的守护进程”,有一个旧的(不流行的)解决方案。它称为 inetd(或 xinetd)。缺点是:

  • 你的守护进程需要在 stdin/stdout 上进行通信(如果你不控制守护进程——如果你没有源代码——那么这可能是一个阻碍,尽管有些服务可能有一个 inetd 兼容性标志)
  • 为每个连接创建一个新的守护进程
  • 这是链条中的一个额外环节

优点:

  • 可在任何旧的 UNIX 上使用
  • 一旦你的系统管理员设置了配置,你就可以开始你的开发了(当你重新构建你的守护进程时,你可能会失去 setcap 功能吗?然后您必须返回管理员“请先生……”)
  • 守护进程不必担心网络问题,只需在 stdin/stdout 上进行对话
  • 可以根据要求配置为以非 root 用户身份执行守护进程

另一种选择:被黑客攻击的代理(netcat 甚至其他东西 更稳健)从特权端口到某个任意高编号端口,您可以在其中运行目标守护程序。(Netcat 显然不是一个生产解决方案,而是“只是我的开发盒”,对吧?)。这样您就可以继续使用服务器的支持网络的版本,只需要 root/sudo 来启动代理(在启动时),而不会依赖于复杂/可能脆弱的功能。

我的“标准解决方法”使用socat作为用户空间重定向器:

socat tcp6-listen:80,fork tcp6:8080

请注意,这不会扩展,分叉是昂贵的,但这是socat的工作方式。

2017 年更新:

使用 授权绑定


比 CAP_NET_BIND_SERVICE 或自定义内核好得多。

  • CAP_NET_BIND_SERVICE 授予对二进制文件的信任,但不提供 控制每个端口的访问。
  • Authbind 将信任授予 用户/组,并提供对每个端口访问的控制,以及 支持 IPv4 和 IPv6(最近添加了 IPv6 支持)。

    1. 安装: apt-get install authbind

    2. 配置对相关端口的访问,例如80 和 443 适用于所有用户和组:

      须藤触摸 /etc/authbind/byport/80
      须藤触摸/etc/authbind/byport/443
      须藤 chmod 777 /etc/authbind/byport/80
      须藤 chmod 777 /etc/authbind/byport/443

    3. 通过执行你的命令 authbind
      (可选地指定 --deep 或其他参数,请参阅手册页):

      authbind --deep /path/to/binary command line args
      

      例如

      authbind --deep java -jar SomeServer.jar
      

作为 Joshua 精彩的(=不推荐,除非你知道自己在做什么)建议破解内核的后续内容:

我先发帖了 这里.

简单的。对于普通或旧的内核,则不需要。
正如其他人指出的,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 用于 mono/.NET。

我也很确定 xinetd 不是最好的主意。
但既然这两种方法都是 hack,为什么不直接通过解除限制来解除限制呢?
没有人说你必须运行普通的内核,所以你可以运行你自己的内核。

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

/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 情况,可以将其更改为:#定义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;如果您想确保此限制不再困扰您,请编译内核。

Linux支持功能,以支持更细粒度的权限,而不仅仅是“此应用程序是以root身份运行。其中一项功能是 CAP_NET_BIND_SERVICE ,它涉及绑定到特权端口(&lt; 1024)。

不幸的是我不知道如何利用它来运行非root用户,同时仍然给它 CAP_NET_BIND_SERVICE (可能使用 setcap ,但必然会有一个现有的解决方案。)

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

快速的答案是获取最新(尚未发布)版本的 libcap 的副本 来自 git 并编译它。复制结果 progs/capsh 二进制某处(/usr/local/bin 是一个不错的选择)。然后,以 root 身份启动您的程序

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

这里发生了很多事情。

首先,我们以 root 身份运行,因此默认情况下,我们可以获得全套功能。其中包括切换 uid 和 gid 的能力 setuidsetgid 系统调用。然而,通常当程序执行此操作时,它会失去其功能集 - 这就是使用旧方法删除 root 的原因 setuid 仍然有效。这 --keep=1 旗帜告诉 capsh 发出 prctl(PR_SET_KEEPCAPS) 系统调用,它会在更改用户时禁止删除功能。用户的实际变化 capsh 发生与 --user 标志,运行 setuidsetgid.

我们需要解决的下一个问题是如何以一种在我们完成之后继续进行的方式设置能力 exec 我们的孩子。功能系统始终具有一组“继承”的功能,即“跨 execve(2) 保留的一组功能”[能力(7)]。虽然这听起来像是解决了我们的问题(只需设置 cap_net_bind_service 继承的能力,对吧?),这实际上只适用于特权进程 - 我们的进程不再有特权,因为我们已经更改了用户(使用 --user 旗帜)。

新的环境功能集解决了这个问题 - 它是“在非特权程序的 execve(2) 中保留的一组功能”。通过把 cap_net_bind_service 在环境集中,当 capsh exec 是我们的服务器程序,我们的程序将继承此功能并能够将侦听器绑定到低端口。

如果您有兴趣了解更多信息,请了解以下功能 手册页 对此进行了非常详细的解释。跑步 capsh 通过 strace 也很翔实!

总而言之:对于“答案”(如我所见),请跳至此答案中的 >>TLDR<< 部分。

好吧,我已经弄清楚了(这次是真的),这个问题的答案,我的这个答案也是一种为推广道歉的方式 另一个答案 (在这里和推特上)我认为是“最好的”,但在尝试之后,发现我错了。孩子们从我的错误中吸取教训:在您亲自尝试之前不要推销某些东西!

我再次回顾了这里的所有答案。我试过了 一些 其中(并选择不尝试其他解决方案,因为我根本不喜欢这些解决方案)。我认为解决方案是使用 systemd 以其 Capabilities=CapabilitiesBindingSet= 设置。经过一段时间的挣扎后,我发现这不是解决方案 因为:

功能旨在限制根进程!

正如OP明智地指出的那样, 总是 最好避免这种情况(如果可能的话,对于所有守护进程!)。

您不能使用与功能相关的选项 User=Group=systemd 单元文件,因为功能是 总是 重置时 execev (或任何函数)被调用。换句话说,当 systemd 分叉并放弃其权限,功能被重置。没有办法解决这个问题,内核中的所有绑定逻辑都是围绕 uid=0 的,而不是功能。这意味着能力不太可能成为这个问题的正确答案(至少在短期内)。顺便, setcap, 正如其他人提到的,这不是一个解决方案。它对我不起作用,它不能很好地与脚本配合使用,并且每当文件更改时这些脚本都会重置。

在我微不足道的辩护中,我确实声明(在我现已删除的评论中),詹姆斯 iptables 建议(OP也提到)是“第二最佳解决方案”。:-P

>>TLDR<<

解决方案是结合 systemd 与即时 iptables 命令,像这样(取自 DNSChain):

[Unit]
Description=dnschain
After=network.target
Wants=namecoin.service

[Service]
ExecStart=/usr/local/bin/dnschain
Environment=DNSCHAIN_SYSD_VER=0.0.1
PermissionsStartOnly=true
ExecStartPre=/sbin/sysctl -w net.ipv4.ip_forward=1
ExecStartPre=-/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=-/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStartPre=/sbin/iptables -A INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStopPost=/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStopPost=/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
User=dns
Group=dns
Restart=always
RestartSec=5
WorkingDirectory=/home/dns
PrivateTmp=true
NoNewPrivileges=true
ReadOnlyDirectories=/etc

# Unfortunately, capabilities are basically worthless because they're designed to restrict root daemons. Instead, we use iptables to listen on privileged ports.
# Capabilities=cap_net_bind_service+pei
# SecureBits=keep-caps

[Install]
WantedBy=multi-user.target

在这里我们完成以下任务:

  • 该守护进程侦听 5333,但由于以下原因,在 53 上成功接受了连接 iptables
  • 我们可以将命令包含在单元文件本身中,这样就可以省去人们的麻烦。 systemd 为我们清理防火墙规则,确保在守护进程未运行时将其删除。
  • 我们从不以 root 身份运行,并且我们使权限升级变得不可能(至少 systemd 声称),据说即使守护进程受到损害并设置 uid=0.

iptables 不幸的是,它仍然是一个相当丑陋且难以使用的实用程序。如果守护进程正在监听 eth0:0 代替 eth0, ,例如,命令是 稍微不一样.

systemd 是一个sysvinit替代品,可以选择启动具有特定功能的守护程序。选项Capabilities =,CapabilityBoundingSet =在 systemd.exec(5)联机帮助页中。

端口重定向对我们来说最有意义,但我们遇到了一个问题,我们的应用程序将在本地解析一个也需要重新路由的URL; (这意味着你 shindig )。

这也允许您在访问本地计算机上的URL时重定向。

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

由于某种原因,没有人提到将 sysctl net.ipv4.ip_unprivileged_port_start 降低到您需要的值。例子:我们需要将我们的应用程序绑定到 443 端口。

sysctl net.ipv4.ip_unprivileged_port_start=443

有人可能会说,这存在潜在的安全问题:非特权用户现在可以绑定到其他特权端口 (444-1024)。但是你可以使用 iptables 轻松解决这个问题,通过阻止其他端口:

iptables -I INPUT -p tcp --dport 444:1024 -j DROP
iptables -I INPUT -p udp --dport 444:1024 -j DROP

与其他方法的比较。这个方法:

  • 从某种意义上说(IMO)比设置 CAP_NET_BIND_SERVICE/setuid 更安全,因为应用程序根本不 setuid,甚至部分不设置(功能实际上是)。例如,若要捕获启用功能的应用程序的核心转储,需要更改 sysctl fs.suid_dumpable(这会导致另一个潜在的安全问题) 此外,当设置了 CAP/suid 时,/proc/PID 目录归 root 所有,因此您的非 root 用户将无法完全了解/控制正在运行的进程,例如,用户将无法(在通常情况下)通过 /proc/PID/fd/ (netstat -aptn | grep PID) 确定哪些连接属于应用程序。
  • 存在安全缺点:当您的应用程序(或任何使用端口 443-1024 的应用程序)由于某种原因关闭时,另一个应用程序可能会占用该端口。但这个问题也可能适用于 CAP/suid(如果你在解释器上设置它,例如java/nodejs) 和 iptables-redirect。使用systemd-socket方法可以排除这个问题。使用 authbind 方法仅允许特殊用户绑定。
  • 不需要在每次部署新版本的应用程序时设置 CAP/suid。
  • 不需要应用程序支持/修改,如 systemd-socket 方法。
  • 不需要内核重建(如果运行版本支持此 sysctl 设置)
  • 不像 authbind/privbind 方法那样执行 LD_PRELOAD,这可能会影响性能、安全性、行为(是吗?没有测试过)。在其他方面,authbind 确实是灵活且安全的方法。
  • 优于 iptables REDIRECT/DNAT 方法,因为它不需要地址转换、连接状态跟踪等。这仅在高负载系统上才会明显。

根据情况,我会在 sysctl、CAP、authbind 和 iptables-redirect 之间进行选择。我们有这么多选择,这真是太好了。

启动时:

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

然后你可以绑定到你转发的端口。

使用systemd,您只需稍微修改您的服务即可接受预先激活的套接字。

您稍后可以使用 systemd socket activate

不需要任何功能,iptables或其他技巧。

这是来自简单 python http服务器

文件 httpd-true.service

[Unit]
Description=Httpd true 

[Service]
ExecStart=/usr/local/bin/httpd-true
User=subsonic

PrivateTmp=yes

文件 httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

还有'djb方式'。您可以使用此方法以root身份在tcpserver下的任何端口上运行启动流程,然后它将在流程启动后立即将流程控制权交给您指定的用户。

#!/bin/sh

UID=`id -u yourusername`
GID=`id -g yourusername`
exec tcpserver -u $UID -g $GID -RHl0 0 portnumber   /path/to/your/process &

有关详细信息,请参阅: http://thedjbway.b0llix.net/daemontools/uidgid。 HTML

使用 privbind 实用程序:它允许非特权应用程序绑定到保留端口。

由于OP只是开发/测试,不太流行的解决方案可能会有所帮助:

setcap可以在脚本的解释器上使用,以向脚本授予功能。如果全局解释器二进制文件上的setcaps不可接受,则制作二进制文件的本地副本(任何用户都可以)并在此副本上获取root用户的setcap。 Python2(至少)与脚本开发树中的解释器的本地副本一起正常工作。不需要suid,因此root用户可以控制用户可以访问的功能。

如果您需要跟踪解释器的系统范围更新,请使用以下shell脚本来运行脚本:

#!/bin/sh
#
#  Watch for updates to the Python2 interpreter

PRG=python_net_raw
PRG_ORIG=/usr/bin/python2.7

cmp $PRG_ORIG $PRG || {
    echo ""
    echo "***** $PRG_ORIG has been updated *****"
    echo "Run the following commands to refresh $PRG:"
    echo ""
    echo "    $ cp $PRG_ORIG $PRG"
    echo "    # setcap cap_net_raw+ep $PRG"
    echo ""
    exit
}

./$PRG $*

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

我还发现PREROUTING REDIRECT不适用于机器内启动的连接。要使用本地计算机中的连接,请同时添加OUTPUT规则&#8212;请参阅 iptables端口重定向不适用于localhost 。例如。类似的东西:

iptables -t nat -I OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080

我还发现PREROUTING REDIRECT 也会影响转发的数据包。也就是说,如果机器也在接口之间转发数据包(例如,如果它充当连接到以太网的Wi-Fi接入点),那么iptables规则也将捕获连接客户端到Internet目的地的连接,并将它们重定向到机器。这不是我想要的&#8212;我只想重定向指向机器本身的连接。我发现通过添加 -m addrtype --dst-type LOCAL ,我可以使它只影响发送到盒子的数据包。例如。类似的东西:

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
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top