让我尝试解释一下我的家庭网络设置:
┌────────────────────┐
│ Internet │
│ Public IP: 1.2.3.4 │
└──────────┬─────────┘
│
┌──────────────────┴─────────────────┐
│ ISP Modem │
│ Forward everything to AP Router │
│ 192.168.1.1 │
└──────────────────┬─────────────────┘
│
┌─────────────────┴───────────────┐
│ AP Router │
│ DHCP happens here │
│ Forward 1122 to 192.168.10.2:22 ├─────────────┐
│ 192.168.10.1 │ │
└─────────────────┬───────────────┘ │
│ │
│ │
│ ┌───────┴───────┐
│ │ NUC (Ubuntu) │
│ │ PiHole + VPN │
│ │ 192.168.10.50 │
│ └───────────────┘
│ ▲
│ │
┌────────────────────┴──────────────────┐ │
│ Desktop (Ubuntu) │ │ Default routing
│ 192.168.10.2 │ │
│ Default gateway: 192.168.10.50 ├──────────┘
│ DNS: 192.168.10.50 │
└───────────────────────────────────────┘
如果桌面用作192.168.10.1
默认网关,例如,SSH 可以1.2.3.4:1122
工作,我可以通过 SSH 连接到桌面。但我希望将桌面用作192.168.10.50
默认网关。在这种情况下,任何端口转发都不起作用。
经过一些研究后,这可以通过基于 IP 表/策略的路由来完成,但我对此一无所知。最简单的方法是什么?
答案1
TL;DR(仅第一种方法)
在桌面上:
ip route add 192.168.10.0/24 dev eth0 table 1000
ip route add default via 192.168.10.1 dev eth0 table 1000
ip rule add iif lo ipproto tcp sport 22 lookup 1000
问题
这里的问题发生在桌面上。
通过不同的布局,NUC 可以可靠地拦截所有流量,从而可以使用更简单的方法。这需要 NUC 拥有两个网络设备,因为在同一以太网 LAN 上路由两个 IP LAN 并不能防止 DHCP 等问题。将 NUC 作为有状态网桥是另一种解决方案,也需要两个 NIC。
在当前的布局下,NUC 无法拦截 AP 和桌面之间的所有流量......
...该解决方案必须在桌面上完成。
Linux可以使用策略路由其中选择器用于为数据包提供不同的结果(通过使用不同的路由表)。所有关于对明显相同的目的地使用多个路由的问题都需要使用策略路由,主要是使用能够根据源进行分离的选择器(因为路由表已经在这里用于分离目的地)。
人们必须以某种方式将直接来自 AP 的数据包与来自 NUC 的数据包分开,这样当涉及到桌面的 SSH 连接时,它们可能会产生不同的结果(即:不同的路由)。
似乎不可用的ip rule
是一个选择器,当这些路由仅与所使用的网关不同时,可以区分通过两条路由到达的两个数据包。 Linux 的策略规则似乎没有捕捉到这种情况:只要它来自同一接口,它就是相同的。
我假设:
- 桌面的网络接口称为以太网0。
- 桌面不是路由(例如:libvirt、LXC、Docker)。路由需要更多配置并选择应该做什么(VM 应该接收来自 NUC 还是来自 AP 的 SSH?)。下面的答案需要一些小的调整才能正确地为路由情况创建例外,或者容器/虚拟机将只遵循默认路由(即:通过 NUC)。
这里有两种方法。
策略路由匹配四层协议(TCP 22端口)
自从Linux 4.17可以使用选择器在 TCP 端口 22 上与策略路由进行匹配。然后很容易使用不同的路线。不要以不同的方式处理数据包的来源,而是以不同的方式处理此特定端口:
ip route add 192.168.10.0/24 dev eth0 table 1000
ip route add default via 192.168.10.1 dev eth0 table 1000
ip rule add iif lo ipproto tcp sport 22 lookup 1000
这里iif lo
并不是真正关于罗接口而是具体的语法含义从本地系统。 LAN 路由也必须复制,或者例如来自 NUC 本身的 SSH 连接将通过 AP 进行回复,AP 会发出 ICMP 重定向以告知配置错误。在这种特定情况下,不需要规则来为接收到的数据包指定备用路由,因为它是同一接口。如果是其他接口并且SRPF启用(rp_filter=1
),ip rule add iif eth0 ipproto tcp dport 22 lookup 1000
替换eth0
为规则中的实际其他接口,并且还需要默认路由。
这是一个非常简单的方法,只需 3 个命令即可实现目标。
可以对此进行调整,以便在 VPN 允许传入流量的情况下从某些特定 LAN 或来自 NUC 的地址块接收 SSH,但这在任何情况下都不允许从使用这两个 IP 的同一个公共 IP 源接收 SSH 连接同时指定目的地/路线。
使用AP的MAC地址和标记进行策略路由
与之前的方法不同,有一种间接方法可以将传入数据包识别为来自 AP 网关而不是来自 NUC:其以太网源 MAC 地址。
这不能直接由策略路由使用,但可以使用防火墙标记来标记此类传入数据包。标记能由策略路由使用,并且有多种方法可以在回复数据包上设置此标记。
我将把传入部分和回复部分分开。由于这不依赖于特定类型的传入流量,因此无需进行任何更改即可处理稍后从 AP 转发到桌面的其他端口。
我将假设如下:
- AP 的 MAC 地址(如 ping 后在桌面上看到的那样
ip neigh show 192.168.10.1
)的值为 02:00:00:ac:ce:55。替换下面的这个值。
传入和常用设置
人们应该看看 Netfilter 是如何工作的,iptables和路由交互这个示意图:
一个iptablesraw/PREROUTING 中的规则将标记数据包。然后,这通过策略路由以与之前类似的方式完成。
iptables -t raw -A PREROUTING -i eth0 -m mac --mac-source 02:00:00:ac:ce:55 -j MARK --set-mark 1
ip route add default via 192.168.10.1 table 1000
ip rule add fwmark 1 lookup 1000
回复
有两种处理回复的方法:
简单且自动, 仅 TCP
只能与 TCP 一起使用,不能与其他协议一起使用,包括不能与 UDP 一起使用。
由于目标是 TCP 端口 22,这对于 OP 的情况来说已经足够了。只需完成传入部分:
sysctl -w net.ipv4.tcp_fwmark_accept=1 sysctl -w net.ipv4.fwmark_reflect=1
说明:
-
每个 TCP插座接受新连接时创建的将继承第一个数据包的标记,就好像
SO_MARK
套接字选项仅用于此连接一样。具体地,这里,当设置标记时,使用路由表1000,所有回复流量将通过传入流量到达的同一网关路由回。 -
以类似的方式,由内核直接处理的回复数据包(例如 ICMP 回显回复或 TCP RST 以及 TCP FIN 的某些情况)继承传入数据包的标记。例如,如果没有 TCP 套接字侦听(即:桌面上的 SSH 服务器已停止),就会出现这种情况。如果没有此标记,通过 AP 的 SSH 连接尝试将会超时,而不是获得连接连接被拒绝因为 TCP RST 将通过 NUC 路由(并被远程客户端忽略)。
或者相反...
-
通用处理通过转移标记数据包和之间连线进入并返回回复包
A标记可以记为康马克在一个连线通过将其复制回 mangle/OUTPUT 中,使其影响流的所有其他数据包(包括回复数据包)连线到标记。完成传入部分:
iptables -t mangle -A PREROUTING -m mark --mark 1 -j CONNMARK --set-mark 1 iptables -t mangle -I OUTPUT -m connmark --mark 1 -j MARK --set-mark 1
这将处理所有情况(包括 TCP RST 和 UDP)。因此,AP 可以配置为将任意传入的 TCP 或 UDP 流量转发到桌面。此博客中的其他文档。
各种各样的
注意事项
当地址被删除(然后可能被添加回来)或接口被关闭(然后打开)时,手动添加的所有关联路由都将被删除并且不会重新出现。因此,手动
ip route
命令至少应该与配置桌面网络的工具集成,以便每次建立网络连接时添加它们。每个工具都有不同的方法来进行高级网络配置,这可能不完整。例如Ubuntu的网络计划
routing-policy
如果可以使用iif lo
或 ,则不会在其设置中记录ipproto tcp sport 22
。应首选允许使用自定义脚本来替换不可用功能的工具(例如ifupdown
或者NetworkManager
可以做到这一点)。吹毛求疵:对于使用最后一种方法的极其复杂的情况,其中单个远程(公共)IP 地址将使用两条路由(视为两个不同的公共 IP 地址)两次连接到同一桌面服务,以防 VPN 允许传入流量,并为两个目标使用相同的源端口,桌面只会看到两次相同的流量,并且会感到困惑(两个 UDP 将被合并,第二个 TCP 将失败)。这通常可以在路由时处理(使用连线区域和/或自动具有连线更改源端口),可能无法为此处的主机情况处理此问题。
奖金
如果 Desktop 实际上是路由器,则应更改最后一个使用标记和 CONNTRACK 的方法。到容器的路由必须复制到表 1000。这应该可行,但尚未使用 Docker 进行测试(这可能会带来额外的挑战)。
这里假设:
- Desktop 通过名为的接口在 LAN 172.17.0.0/16 中路由经过 NAT 处理的容器br0(Docker 会使用码头工人0对于默认网络),本地 IP 地址为 172.17.0.1/16
- 桌面 DNAT 将某些端口通向这些容器
变化:
规则和路线
到容器的路由必须从主路由表复制到表 1000。如果容器/虚拟化工具动态添加新的接口和路由,则必须手动添加新路由(或使用工具中的某些 API 触发的某些脚本化机制)也被添加到表1000中。
ip route add 172.17.0.0/16 dev br0 table 1000
如果没有这个,通过 AP 并标记(在下一个项目符号中)的传入连接将被路由后退到美联社。
保留之前有关 MAC 地址的规则生的桌子。
删除之前的规则碾压桌子
iptables -t mangle -F
把这些规则改为:
iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j CONNMARK --save-mark iptables -t mangle -A PREROUTING -m connmark ! --mark 0 -j CONNMARK --restore-mark iptables -t mangle -A OUTPUT -m connmark ! --mark 0 -j CONNMARK --restore-mark
(对于这种单标记情况,可以以更多行为代价来完成一些优化)
第一个 PREROUTING 规则确保不会用值为 0 的数据包标记覆盖 conntrack 标记。第二个 PREROUTING 规则为来自最初通过 AP 建立的流的容器(单个数据包最初未标记)部分的路由流量设置标记。