完整的“cgroup”解决方案

完整的“cgroup”解决方案

是否可以通过特定接口路由某个进程使用的流量?

例如,下载应用程序的网络流量应始终使用接口,wlan0而机器上的所有其他应用程序都应使用eth0

在Linux中可以有这种规则吗?

答案1

我为此苦苦挣扎,所以这里有一个完整的解决方案。它已在 Ubuntu 15 到 19.10 上测试过。您尤其可以将其与 OpenVPN 一起使用,以将某些应用程序路由到 VPN 隧道接口之外。

完整的“cgroup”解决方案

它是如何工作的?

  • Linux 内核将把应用程序放入控制组。来自此 cgroup 中应用程序的网络流量将通过网络控制器级别的类 ID 进行识别。
  • iptables 将标记此流量并强制其以正确的 IP 退出
  • ip route 将在不同的路由表中处理标记的流量,并使用默认路由到您想要的任何网关 IP。

自动化脚本

我做了一个novpn.sh用于自动安装和运行依赖项的脚本。已在 Ubuntu 15 至 19.10 上测试。

首先启动您的 VPN。

wget https://gist.githubusercontent.com/kriswebdev/a8d291936fe4299fb17d3744497b1170/raw/novpn.sh
# If you don't use eth0, edit the script setting.
sudo chmod +x novpn.sh
./novpn.sh traceroute www.google.com
./novpn.sh --help

手动操作指南

首先,安装 cgroup 支持和工具:

sudo apt-get install cgroup-lite cgroup-tools

你需要 iptables1.6.0+。获取 iptables 1.6.0 发布源,提取它,然后--disable-nftables从 iptables 源目录运行此命令(标志将避免错误):

iptables --version
sudo apt-get install dh-autoreconf bison flex
./configure --prefix=/usr      \
            --sbindir=/sbin    \
            --disable-nftables \
            --enable-libipq    \
            --with-xtlibdir=/lib/xtables
make
sudo make install
iptables --version

现在,真正的配置。定义一个名为的控制组novpn。此 cgroup 中的进程将具有 classid 0x00110011(11:11)。

sudo su
mkdir /sys/fs/cgroup/net_cls/novpn
cd /sys/fs/cgroup/net_cls/novpn
echo 0x00110011 > net_cls.classid

现在,我们假设您想要用于特定应用程序的实际接口的eth0网关 IP 为10.0.0.1代替这些是你真正想要的(从中获取信息ip route),特别是在较新的 Ubuntu 版本中,接口有奇怪的名称。仍然以 root 身份运行:

# Add mark 11 on packets of classid 0x00110011
iptables -t mangle -A OUTPUT -m cgroup --cgroup 0x00110011 -j MARK --set-mark 11

# Force the packets to exit through eth0 with NAT
iptables -t nat -A POSTROUTING -m cgroup --cgroup 0x00110011 -o eth0 -j MASQUERADE

# Define a new "novpn" routing table
# DO THIS JUST ONCE !
echo 11 novpn >> /etc/iproute2/rt_tables

# Packets with mark 11 will use novpn
ip rule add fwmark 11 table novpn

# Novpn has a default gateway to the interface you want to use
ip route add default via 10.0.0.1 table novpn

# Unset reverse path filtering for all interfaces, or at least for "eth0" and "all"
for i in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $i; done

最后,在特定界面上运行你的应用程序:

exit
sudo cgcreate -t $USER:$USER -a $USER:$USER -g net_cls:novpn
cgexec -g net_cls:novpn traceroute www.google.com
# Close all Firefox windows first
killall firefox; cgexec -g net_cls:novpn firefox

或者如果您想将已经运行的进程移至 cgroup,那么......你不能!这似乎是由于 NAT(伪装)功能造成的:iptables -nvL -t nat在切换 cgroup 时不匹配,但iptables -nvL -t mangle确实匹配。

# Get PID of the process (we'll then suppose it's 1234)
pidof firefox
# Add to cgroup - THIS DOESN'T WORK! Silently fails to produce the final result.
sudo echo 1234 > /sys/fs/cgroup/net_cls/novpn/tasks
# Remove - but this works...
sudo echo 1234 > /sys/fs/cgroup/net_cls

致谢:没有答案按预期工作,但其中的一些确实做到了:chripell 答案 evolware 文章每个进程路由需要 2:使用 cgroups、iptables 和策略路由如何使特定进程不通过 OpenVPN 连接?基于 iptables 的 OpenVPN 终止开关

相关问题:iptables 和 cgroups v2(netfilter 的 xt_cgroup)

答案2

可以使用 Linux 网络命名空间来完成。

这是一篇文章这解释了如何操作。基本上,您创建一个具有不同默认路由的网络命名空间,并在那里运行需要它的进程。您使用网桥将新创建的网络命名空间连接到物理适配器(但当然也可以使用其他解决方案)。

更新:从内核 3.14 开始,使用控制组变得更加容易,如本文。 你必须:

1)定义一个 net_cls 控制组来注释来自具有 classid 的给定进程(或进程组,注意它们之间不能有任何父子关系)的数据包

2)使用 iptables cgroup 模块(在 Linux 3.14 中添加)对数据包进行 fwmark

3)使用策略路由(ip rule add fwmark ....)为标记的数据包创建新的路由表

优点是我们不必进行桥接,而且由于 cgroups,一切都变得更加动态。

答案3

一些人编写了使用 Linux 的 LD_PRELOAD 特性的垫片来实现这一点:

答案4

结合 mariusmatutiae 和 KrisWebDev 的出色回答,我创建了一个经过大量修改的 KrisWebDev 出色脚本版本novpn.sh。KrisWebDev 的脚本旨在解决更具体的问题(在 VPN 内部/外部运行和移动进程),而我的版本允许您在指定的网络环境中运行基本上任何命令。您可以指定要绑定到的接口、默认路由、指定自己的 iptables 规则、静态路由、在运行命令之前指定“测试”以确认一切按您的意愿运行……等等)。它允许您使用多个配置文件,以便您可以定义任意数量的特定网络环境,您可以在其中运行命令/进程。

我将其作为要点发布在这里: https://gist.github.com/level323/54a921216f0baaa163127d960bfebbf0

它甚至可以随后清理 cgroup/iptables/路由表!

欢迎反馈。

PS-它是为 Debian 8 设计的(Jessie)

相关内容