我这里有一个艰难的人,但似乎无法找出正确的路线。
我有一个位于两个独立网络上的服务器 (serverA):
192.168.200.x/24
& 192.168.117.0/26
。此服务器有一个主机名 ( serverA.example.com
)192.168.117.70
和一个单独的连接通过192.168.200.45
(这是在此网络上安装存储/等所必需的)。
我没有办法在内部执行 split-DNS,因此我需要将所有内容路由到此 IP/主机名以保证用户的安全。
目前,所有不在网络上的东西192.168.200.x
都可以serverA.example.com
正常访问。但是 上的所有系统192.168.200.x
都无法路由到该IP 地址,但它们可以正常192.168.117.70
访问。192.168.200.45
这是我在 serverA 上设置的路由:
0.0.0.0 192.168.117.65 0.0.0.0 UG 0 0 0 onboard-10Gb-1
192.168.117.64 0.0.0.0 255.255.255.192 U 0 0 0 onboard-10Gb-1
192.168.200.0 0.0.0.0 255.255.255.0 U 0 0 0 bond0
我在路由等方面没有太多经验,所以我知道我在这里遗漏了一些东西。
长话短说
有关详细信息以及正确处理 UDP 服务的信息,请阅读下文。
注意:在 Linux 上,
route
和ifconfig
是过时的命令。它们不适用于策略路由等高级路由。应该系统地使用iproute2替代品:ip route
,ip link
和(以及iproute2ip address
套件中的所有其他相关命令)。策略路由
当从那里查询时,多宿主服务器默认直接回复连接的 LAN 192.168.200.0/24(下面的示例将使用位于 192.168.200.101 的假设系统),而不是遵循查询来自的路径。这是一个非对称流,可能会因各种原因而失败,其中包括:
rp_filter=1
规则丢弃此类不对称流量。即使没有前 3 种情况,TCP 也可能工作,UDP 在最后一种情况下更加困难,单独的策略路由对于 UDP 来说通常并不总是足够的(见下面的警告),但仍然是必需的。
这需要策略路由,以便在进行路由决策以回复时单独考虑每个地址。
在 Linux 上,这是通过使用带有选择器的路由规则来实现的,以使用备用路由表,这些路由表将只知道选定目标所需的路径:仅是所有可能路由的部分副本。所选择的选择器通常是一个标准,取决于目的地以外的其他事物(已经提供了标准路由条目)。大多数时候它是源地址,但这取决于目标。
这里的目标是拥有一个不具体了解 192.168.200.0/24 的路由表,以便在从 192.168.117.70 做出回复时使用默认路由
onboard-10Gb-1
而不是 LAN 路由进行路由。bond0
只复制路由表 1000 中需要的路由(值 1000 任意选择):
当源地址是192.168.117.70,即是服务器地址时,先查备选路由表1000(查主路由表前:如果查到路由成功,主表就不用了) :
可以添加其他 LAN 的等效表和规则,但它已经由主路由表处理。传入流量已首先由本地路由表处理,因此与此设置无关:
然后在server上,取决于服务的使用方式:
TCP 服务绑定或不绑定到特定地址始终有效
任何
accept(2)
-ed 套接字都将其源(本地)地址设置为查询使用的目标地址,因此发出的数据包将在需要时匹配路由规则选择器:对于这种情况无需做更多的事情。TCP客户端或UDP客户端在查询/连接时可以绑定源地址,改变路径:
TCP 和 UDP 示例:
预期的备用路由表 1000 将根据所选的源地址进行选择:
警告:UDP 服务
UDP 和 BSD 套接字 API 的工作方式,默认情况下不绑定地址的 UDP 套接字(即使用 0.0.0.0 又名 INADDR_ANY),当它用于回复收到的 UDP 消息时,没有查询的所有上下文,特别是它不会有查询发送到的服务器的本地地址,与 TCP 相反:它只会使用套接字的 0.0.0.0 地址。
因此,当回复从 192.168.200.101 到 192.168.117.70 的查询时,它将向路由堆栈提供一个 0.0.0.0 的源,以推迟路由堆栈对实际源地址的选择。这不会匹配 192.168.117.70 的路由规则选择器,回复将使用主路由表,选择错误的源回复地址:192.168.200.45。当客户端收到这样的回复(直接来自同一个 LAN)时,它不会将其识别为对其查询的回复:它来自其他地址,并将忽略它。
有两种方法可以让 UDP 服务器应用程序正确处理此问题:
bind(2)
到一个特定的地址。使用此套接字的任何答复都将使用它绑定到的地址。从而选择路由规则和预期的路由行为。如果服务器必须在它的所有地址上提供服务,它应该多次绑定相同的方式:每个地址一次。
大多数守护进程中都有设置可以做到这一点。例如,默认情况下,ISC 的 DNS 服务器bind 9绑定到属于该服务器的所有地址并跟随动态变化。当查询到达这样的套接字时,绑定套接字的地址就是查询的目的地地址。它将被重新用作源地址,从而选择正确的路由规则。
否则使用套接字选项
IP_PKTINFO
这使得应用程序能够接收辅助数据,使其知道在哪个地址和哪个接口上接收到数据包,并提供所有信息以进行正确回复。这需要特定的应用程序支持,包括使用附加功能,例如
cmsg(3)
.例如,当使用服务器选项时,这就是 NLnet Labs 的 DNS 服务器未绑定的操作模式
interface-automatic: yes
。如果 UDP 服务器应用程序根本无法更改,那么就只剩下糟糕的选择了。
使用Netfilter的conntrack和iptables将不起作用:可以更改输出挂钩中的目标,但必须更改的是源。可以在postrouting挂钩中更改源,但顾名思义,它是在路由之后:选择备用路由为时已晚。即使它被允许,因为它是关于对现有流的回复的NAT , Netfilter也不会正确处理它并且会更改用于回复的源端口以避免仅假定的冲突而不是重用相同的流.
在这种情况下,可以使用额外的选择器,根据服务和目的地(在本例中为回复)选择备用源选择器,以强制进行其他选择:源 192.168.117.70 而不是 192.168.200.45 (现在从 192.168.200.101 到 192.168.200.45 的直接查询会因为类似的原因而失败)。
例如,如果服务器要在端口 5555 上托管一个简单的 UDP 服务,该服务不能配置为绑定到 192.168.117.70 或使用
IP_PKTINFO
并且永远不应该在 192.168.200.0/24 上直接使用 LAN 到 LAN,可以轻推正确的路由选择(这需要内核 >= 4.17):这里 0.0.0.0/32 用于其 INADDR_ANY 角色。路由堆栈最终会用足够的源替换它,但这次选择使用路由表 1000。
前:
后:
不会影响其他情况(如:源端口5556):
nftables
实际上,还存在一个较差的解决方案作为
ip rule
上面添加的第二个的替代方案,使用nftables代替执行静态 NAT 不依赖于Netfilter的conntrack。这是要加载的规则集nft -f ...
:类型路由挂钩将负责重新路由现在将遍历路由表 1000 的数据包。
ip rule
除非需要无法使用的复杂过滤器,否则最好使用路由规则。