![在 Linux 上“离线”(无外部网络)运行子进程的命令](https://linux22.com/image/51578/%E5%9C%A8%20Linux%20%E4%B8%8A%E2%80%9C%E7%A6%BB%E7%BA%BF%E2%80%9D%EF%BC%88%E6%97%A0%E5%A4%96%E9%83%A8%E7%BD%91%E7%BB%9C%EF%BC%89%E8%BF%90%E8%A1%8C%E5%AD%90%E8%BF%9B%E7%A8%8B%E7%9A%84%E5%91%BD%E4%BB%A4.png)
我有一个程序想在离线模式下测试,而不需要关闭我的实际网络。该程序仍然需要连接到本地套接字,包括 unix 域套接字和环回。它还需要听环回并对其他应用程序可见。
但尝试连接到远程计算机应该会失败。
我想要一个实用程序,其工作方式类似于strace
//并简单地运行一个隐藏互联网(和 LAN)的命令,而其他一切仍然正常工作unshare
:sudo
$ offline my-program-to-test
这个问题已经暗示了答案:阻止进程的网络访问?
那里有一些建议,例如以另一个用户身份运行然后操作 iptables,或者unshare -n
.但在这两种情况下,我都不知道与主系统共享 unix 域套接字和环回的咒语 - 该问题的答案仅告诉我如何取消共享全部的网络。
我正在测试的程序仍然需要连接到我的 X 服务器和 dbus,甚至能够侦听来自系统上其他应用程序的环回连接。
理想情况下,我希望避免创建 chroot、用户、虚拟机等,因为它变得和拔掉网络电缆一样烦人。即问题的重点是我怎样才能使它像sudo
.
我希望该进程能够 100% 正常运行,除非指定非本地地址的网络调用会失败。理想情况下保持相同的 uid、相同的 homedir、相同的 pwd、相同的一切,除了……离线。
我使用的是 Fedora 18,因此不可移植的 Linux 答案就很好(甚至是预期的)。
我什至很高兴通过编写 C 程序来解决这个问题,如果涉及到的话,那么涉及编写 C 的答案就可以了。我只是不知道 C 程序需要进行哪些系统调用来撤销外部网络访问,同时保留本地网络。
任何尝试支持“离线模式”的开发人员可能都会喜欢这个实用程序!
答案1
传统的答案是以另一个用户身份运行该程序并使用iptables -m owner
.这样,网络配置就可以共享。然而,随着命名空间的出现,有一种更简单的方法。
使用命名空间,您可以取消网络共享,然后在需要有限的网络访问时创建虚拟网络链接。
要共享 unix 域套接字,您所需要的只是拥有一个足够新的内核,之后这个2010年的补丁,所以 2.6.36 或更高版本(在撰写本文时,除 RHEL/CentOS 之外的所有当前发行版都是这种情况)。
在自己的 IP 命名空间中运行该程序。在启动程序之前,建立一个虚拟以太网接口。似乎没有太多文档;我在一些博客中找到了正确的咒语:
- Arista 的 Linux 命名空间
- 关于veth接口的一些注释通过瓦尔德纳
- Linux 网络命名空间简介通过斯科特·洛
在下面的代码片段中,我使用的ns_exec
是这个包装纸setns
手册页中列出了从受限制的命名空间中运行的进程启动网络链接的主机端。您实际上不需要这个:您可以从受限命名空间外部设置链接的主机端。从内部执行此操作只会促进流量控制,否则您需要一些同步来在建立链接之后但启动程序之前设置链接。
unshare -n -- sh -c '
# Create a virtual ethernet interface called "confined",
# with the other end called "global" in the namespace of PID 1
ip link add confined type veth peer name global netns 1
# Bring up the confined end of the network link
ip addr add 172.16.0.2/30 dev confined
ip link set confined up
# Bring up the global end of the network link
ns_exec /proc/1/ns/net ifconfig global 172.16.0.1 netmask 255.255.255.252 up
# Execute the test program
exec sudo -E -u "$TARGET_USER" "$0" "$@"
' /path/to/program --argument
您需要以 root 身份完成所有这些操作。这内核 3.8 中引入用户命名空间除了设置网络链接的全球末端之外,无需特殊权限即可实现此目的。
我不知道如何在两个命名空间之间共享本地主机。虚拟以太网接口创建点对点桥接。如果需要,您可以使用iptables
转发规则来重定向流量lo
。
答案2
这也可以通过 iptables 规则匹配 cgroup 来实现。我编写了一个工具来抽象这些步骤。虽然它需要 root 权限进行初始设置,并且是一个 setuid 二进制文件,但我认为它提供了一种相当简单的方法来处理问题中的问题。它需要足够新的 iptables 版本并且有适当的 netfilter 模块可用。
答案3
最近遇到这个问题。我使用用户组解决了这个问题
添加一个组“no-external-internet”并将您的用户添加到其中
sudo addgroup no-external-internet sudo adduser $USER no-external-internet
添加
iptables
规则以接受{HOST}
传出流量{PORT}
并拒绝所有其他出站连接sudo iptables -A OUTPUT -m owner --gid-owner no-external-internet -s {HOST} -p tcp --sport {PORT} -j ACCEPT; sudo iptables -A OUTPUT -m owner --gid-owner no-external-internet -j REJECT; sudo ip6tables -A OUTPUT -m owner --gid-owner no-external-internet -j REJECT;
使用用户组运行您的子进程:
sg no-external-internet "your command to run child process"
答案4
/!!\ 这可能不是一个有效的答案,因为这会导致您的所有流量下降,而不仅仅是单个 pid 的流量。
我没有使用过它,但我认为你可能会使用康卡斯特:
https://github.com/tylertreat/康卡斯特
设置为 100% 丢包
再说一次,我没有对此进行测试,但理论上,根据他们的自述文件进行修改,您可以执行以下操作:
Linux
在 Linux 上,您可以用来iptables
丢弃传入和传出的数据包。
$ iptables -A INPUT -m statistic --mode random --probability 1 -j DROP
$ iptables -A OUTPUT -m statistic --mode random --probability 1 -j DROP
或者,您可以使用tc
它支持一些附加选项。
$ tc qdisc change dev eth0 root netem reorder 0 duplicate 0 corrupt 1
重置:
$ tc qdisc del dev eth0 root netem