问题: 如何启动程序,同时确保其网络访问通过特定网络接口绑定?
案件: 我想访问具有相同 IP (192.168.1.1) 的两台不同机器,但可以通过两个不同的网络接口(eth1 和 eth2)访问。
例子:
net-bind -D eth1 -exec {Program 192.168.1.1}
net-bind -D eth2 -exec {Program 192.168.1.1}
以上是我想要的近似值,灵感来自于通过完成的硬件绑定普里默斯伦和优化运行。
挑战:正如建议的相关主题,所使用的接口不是由程序选择,而是由内核选择(因此是上面示例中的预绑定语法)。
我找到了一些相关的解决方案,但都不能令人满意。它们基于通过用户特定的网络黑名单绑定网络接口;即,以只能访问单个特定网络接口的用户身份运行该进程。
答案1
对于 Linux,这个问题已经在 Superuser 上得到了回答 -如何为不同的进程使用不同的网络接口?。
最流行的答案使用一种LD_PRELOAD
技巧来更改程序的网络绑定,但现代内核支持一种更灵活的功能,称为“网络命名空间”,该功能通过程序公开ip
。这回答展示了如何使用它。根据我自己的实验,我做了以下工作(作为 root):
# Add a new namespace called test_ns
ip netns add test_ns
# Set test to use eth0, after this point eth0 is not usable by programs
# outside the namespace
ip link set eth0 netns test_ns
# Bring up eth0 inside test_ns
ip netns exec test_ns ip link set eth0 up
# Use dhcp to get an ipv4 address for eth0
ip netns exec test_ns dhclient eth0
# Ping google from inside the namespace
ip netns exec test_ns ping www.google.co.uk
还可以使用unshare
和nsenter
命令在某种程度上管理网络名称空间。这还允许您为 PID、用户和挂载点创建单独的空间。有关更多信息,请参阅:
答案2
我接受格雷姆的回答;这只是为了解释我对他的建议所做的更改以解决我的问题的后续行动。
我没有将物理接口绑定在命名空间内,而是创建了一对虚拟网络接口,一端位于网络命名空间,另一端位于根。然后,包通过该虚拟网络从命名空间路由到根命名空间,然后路由到物理接口。 - 因此,我能够运行所有普通数据传输,此外还启动只能访问特定接口的进程。
# Create the eth0 network namespace
ip netns add eth0_ns
# Create the virtual network pair
ip link add v_eth0a type veth peer name v_eth0b
# Move v_eth0a to the eth0_ns namespace, the virtual pair is now split
# between two network namespaces.
ip link set v_eth0a netns eth0_ns
# Configure the ends of the virtual network pairs
ip netns exec eth0_ns ifconfig v_eth0a up {{NAMESPACE_IP}} netmask {{NAMESPACE_NETMASK}}
ifconfig v_eth0b up {{ROOT_NS_IP}} netmask {{ROOT_NS_NETMASK}}
# Setup routing from namespace to root
ip netns exec eth0_ns route add default gw {{ROOT_NS_IP}} dev v_eth0a
# Setup IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s {{ROUTE_SOURCE}}/24 -o {{NETWORK_INTERFACE}} -j SNAT --to-source {{ROUTE_TARGET}}
一旦为 eth0 和 eth1 设置了接口,以及它们各自的命名空间 eth0_ns 和 eth1_ns,程序就可以通过以下方式在指定接口上执行:
ip netns exec eth0_ns fish
ip netns exec eth1_ns fish
答案3
解决方案一:预加载特定库
应用程序路由监狱:使用 ld_preload 强制接口网关(好主意,但需要 root 或标记功能)用法详见下面的注释
代理绑定:使用 ld_preload 强制代理到特定应用程序(这是使用代理而不是接口)
强制绑定:有很多功能,但绑定泄漏(不可靠)
绑定接口IP:太简单并且连接泄漏(不可靠)
绑定IP:方式太简单并且连接泄漏(不可靠)
解决方案二:Linux 用户空间
经典的linux用户空间ip网络:很好的解决方案,但需要 root 并且界面只能存在于一个用户空间上
火狱:Firejail 可以强制应用程序使用特定网络,但兼容性有限(例如与 tun 接口不兼容)。 firejail 不需要 root
firejail --dns=8.8.8.8 --noprofile --net=eth0 --ip=192.168.1.1 app-command
带 netns 的 Firejail:Firejail 可以强制应用程序使用单独创建的特定用户空间,这让我们可以在没有 root 的情况下命名空间
firejail --dns=8.8.8.8 --noprofile --netns=nameOfyourNS app-command
有化装舞会和桥梁的消防监狱:Firejail 可以强制应用程序使用与 iptables 伪装的特定接口,这很棒,不需要 root但这需要 ip_forward 并且可能意味着安全影响
firejail --net=br0 firefox
解决方案三:Linux iptables
iptables 可以用于此目的但这需要 ip_forward,如果配置不正确,可能会带来安全影响,示例1,示例2,实施例3,实施例4
解决方案(I、II 和 III)注释:
钢丝卫士
如果您正在使用VPN(尤其是wireguard)并且希望将此解决方案应用于wireguard接口(带有用户空间的wireguard)您可以按照链接的说明创建一个包含 wg 接口(因此仅限于 vpn 接口)的用户空间,也可以将其与firejail --netns=container
无需 root 的情况下使用用户空间相结合。
如何找到接口网关
有很多找到网关的解决方案,这里有一些允许找到所使用的网关的命令
$ route
$ route -n
$ ip rule list
$ ip route show
$ netstat -rn
$ cat /etc/network/interfaces
$ cat /etc/sysconfig/network-scripts/ifcfg-eth0
$ traceroute www.google.com
$ ip route show 0.0.0.0/0 dev eth0
如何使用App-Route-Jail
- 构建应用程序路由监狱
git clone https://github.com/Intika-Linux-Network/App-Route-Jail.git
cd Approute-Root-Jail
chmod 755 make.sh
./make.sh
- 为未来标记的数据包(针对被监禁的应用程序)添加一条路由,在本示例中
192.168.1.1
用作强制网关,此路由规则不会影响其他应用程序,例如,如果您想在系统启动时执行此操作,则只需执行一次每天使用此解决方案
ip rule add fwmark 10 table 100
ip route add default via 192.168.1.1 table 100
- 启动您想要监禁的应用程序
MARK=10 LD_PRELOAD=./mark.so firefox
- 测试WAN IP地址
MARK=10 LD_PRELOAD=./mark.so wget -qO- ifconfig.me
答案4
还有另一种解决方案是预加载。
绑定到接口
绑定到接口加载一个强制程序使用特定接口的库。 intika 强制绑定到特定 IP 地址的答案中的其他 3 个绑定选项。因此,而不是例如 0.0.0.0,这意味着您可以绑定到 192.168.178.68 的所有接口,以绑定仅侦听此接口。但是,当建立连接时,仍然会使用默认路由表并使用其他接口。
BindToInterface 的使用方式类似于BIND_INTERFACE=eth1 LD_PRELOAD=./bindToInterface.so <your command>
.您还可以覆盖 DNS 服务器,当您的默认 DNS 服务器无法通过绑定接口访问时,这非常有用。这加快了连接速度。您可以通过环境变量来控制它DNS_OVERRIDE_IP=8.8.8.8
如果您的程序绑定到 eth1,则它无法到达 localhost。如果它应该到达它或其他 IP,您可以通过排除它们BIND_EXCLUDE=127.0.0.1
。
所以命令看起来像
BIND_INTERFACE=eth1 DNS_OVERRIDE_IP=8.8.8.8 BIND_EXCLUDE=127.0.0.1 LD_PRELOAD=./bindToInterface.so <your command>
.
还有一些更多的选择绑定到 Github 页面上解释的接口。
目前(2020 年 3 月)Firejail 解决方案无法通过 Wifi 实现,请参阅https://github.com/netblue30/firejail/issues/3000