![Linux - 强制不同的用户使用不同的网络接口](https://linux22.com/image/779746/Linux%20-%20%E5%BC%BA%E5%88%B6%E4%B8%8D%E5%90%8C%E7%9A%84%E7%94%A8%E6%88%B7%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%90%8C%E7%9A%84%E7%BD%91%E7%BB%9C%E6%8E%A5%E5%8F%A3.png)
我有一台 Linux 机器,它设置了一个物理网卡,与托管交换机相连。连接是 VLAN 中继。机器上有两个 VLAN 接口,它们有不同的 IP 地址(属于不同的 VLAN)。
我想要实现的是,根据在这台机器上登录的交互式用户,强制用户使用某个VLAN的网络接口。
当前网络设置
NIC = enp1s0
VLAN3 itf = enp1s0.3 - bridged in br0.3 with IP = 192.168.3.2
VLAN4 itf = enp1s0.4 - bridged in br0.4 with IP = 192.168.4.2
用户 3(uid=1003)登录时,他创建的所有网络流量都应通过 br0.3
用户 4 (uid=1004) 登录时,他创建的所有网络流量都应通过 br0.4
选项 1:仅 nft。
我的第一次尝试是使用 nftables 来强制执行此操作。
# hook output, type route (allows to mangle with packets)
nft add table net4
nft add chain net4 mangle '{
type route hook output priority -300; policy accept;
}'
nft add rule ip net4 mangle meta skuid 1004 ip saddr set 192.168.4.2 return
nft add rule ip net4 mangle meta skuid 1004 oif br0.3 drop
这并没有按预期工作。数据包的源地址已更改,但它通过 br0.3 发出。返回数据包通过 br0.4 返回。即使 saddr 已更改,传出的数据包仍然绑定到 br0.3。我通过打开两个终端并使用以下命令看到了这种行为:
tcpdump -i br0.3 -nn host 192.168.3.2 or host 192.168.4.2
tcpdump -i br0.4 -nn host 192.168.3.2 or host 192.168.4.2
根据:https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks输出钩子是后路线决策,因此看来这无法再改变。
我尝试了这样的规则:
nft add rule ip net4 mangle meta skuid 1004 ip saddr set 192.168.4.2 meta oifname set br0.4 return
但是不允许设置 oif 或 oifname。
根据https://www.nftables.org/documentation/HOWTO/netfilter-extensions-HOWTO-4.html#ss4.5有一个 ROUTE 补丁可能提供更改 oif 的功能。但该示例针对的是 iptables,而且我似乎既不能将此补丁用于 nftables,也不能用于 iptables。
我也尝试了另一种 nft 规则集,使用标记结合 ip 规则和 ip 路由:
nft add rule ip net4 mangle meta skuid 1004 ip saddr set 192.168.4.2 meta mark set 4 return
nft add rule ip net4 mangle meta skuid 1004 oif br0.3 drop
ip rule add priority 10000 fwmark 4 table 4
ip route add table 4 default via 192.168.4.1 dev br0.4
正如预期的那样,这也不起作用,因为输出挂钩确实位于路由决策之后。
选项 2:带有少量 nft 的网络命名空间
我尝试了一种替代方案:网络命名空间。
# delete the network interface and bridge
ip link set dev br0.4 down
ip link set enp1s0.4 down
ip link del br0.4
ip link del enp1s0.4
# create netns and create new vlan interface in new netns
NAMESPACE=netns4
ip netns add $NAMESPACE
ip -n $NAMESPACE link set dev lo up
ip link add link enp1s0 name enp1s0.4 netns $NAMESPACE type vlan id 4
ip -n $NAMESPACE link add name br0.4 type bridge
ip -n $NAMESPACE link set dev enp1s0.4 master br0.4
ip -n $NAMESPACE link set enp1s0.4 up
ip -n $NAMESPACE link set br0.4 up
ip -n $NAMESPACE addr add 192.168.4.2/24 dev br0.4
ip -n $NAMESPACE route add default via 192.168.4.1 dev br0.4
当我以 root 身份在命名空间之间来回切换时,一切都按预期进行。如果我将此与不允许用户 4 使用 br0.3 的 nft 规则相结合,我或多或少就得到了我需要的东西。
nft add rule ip net4 mangle meta skuid 1004 oif br0.3 drop
但使用此命名空间设置用户会话并不简单。systemd-logind 不允许使用非默认命名空间创建结果用户会话的配置。同样,图形登录无法为每个用户或用户组配置命名空间。我的系统有 sddm,它允许在 [General] 部分中使用 Namespaces=,但这意味着所有用户都将加入所有指定的命名空间。
终端登录需要使用 sudoers 进行这样的破解(因为配置文件内容以用户身份运行)和文件 /etc/profile.local 并且仅适用于 bash。
uid=`/usr/bin/id -u`
uname=`/usr/bin/id -un`
if [ $uid = 1004 ]; then
namespace=netns4
if [ -r /run/netns/$namespace ]; then
namespace_inode=`ls -i /run/netns/$namespace | cut -d ' ' -f 1`
proc_inode=`ls -l /proc/$$/ns/net | sed 's/.*\[\([0-9][0-9]*\)\]/\1/'`
if [ $proc_inode != $namespace_inode ]; then
uname=`/usr/bin/id -un`
sudo /usr/bin/ip netns exec $namespace /usr/bin/su - $uname "${@}"
fi
fi
fi
并且 sudoers 必须有一行:
user4 ALL = (root) NOPASSWD: /usr/bin/ip netns exec netns4 /usr/bin/su - user4
user4 ALL = (root) NOPASSWD: /usr/bin/ip netns exec netns4 /usr/bin/su - user4 *
我似乎无法对 Xsession /usr/etc/X11/xdm/Xsession 完成同样的操作
有人知道如何实现我最初的要求吗?
答案1
您只需要策略路由
ip route add table 1003 default dev enp1s0.3
ip rule add uidrange 1003-1003 table 1003
ip route add table 1004 default dev enp1s0.4
ip rule add uidrange 1004-1004 table 1004
rule add table main suppress_prefixlength 0
答案2
因此我尝试了一下,并且它确实按预期工作。
我有点难以理解
ip rule add table main suppress_prefixlength 0
我猜测其目的是确保由于某种原因未在表 1003 或表 1004 中匹配的数据包将不会遵循主表中前缀长度为 0 的路由(即默认路由)。