将所有传出 DNS 查询重定向到位于 127.0.0.1:53 的本地存根解析器

将所有传出 DNS 查询重定向到位于 127.0.0.1:53 的本地存根解析器

我正在尝试将 Linux 机器上的所有传出 DNS 查询重定向到本地缓存存根解析器(未绑定)。

iptables -t nat -A OUTPUT -p tcp --dport 53 -j DNAT --to 1.1.1.1:53
iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to 1.1.1.1:53
iptables -t nat -A POSTROUTING -j MASQUERADE

当我使用上述规则时,所有传出的 DNS 查询都会被拦截并重定向到位于 1.1.1.1 的 DNS 服务器

但是,如果我将“1.1.1.1”替换为“127.0.0.1”,则所有 DNS 查询都会失败,并且不会定向到我的本地存根解析器。

我确实传递了下面的 sysctl 参数

sysctl -w net.ipv4.conf.eth0.route_localnet=1

但我的问题仍然一样。有什么指点吗?

答案1

如果使用strace, 和nc/进行调试socat,就会清楚的是,这nat/POSTROUTINGMASQUERADE没有改变最初选择的用于出去的地址。可能是因为它仍然被认为是要“路由”到的本地地址,所以lo不需要更改:该MASQUERADE规则在这里不起作用。

无论如何,事情就是这样发生的。所以在回复时UDP协议查询时,服务器实际上连接回发送数据的源,现在用作目的地。自然地,选择最好的源用于该目的地,相同的本地地址,即不是127.0.0.1。因此,如果使用conntrack -E,示例本地 IP 为 192.0.2.2,目标为 198.51.100.1 UDP 端口 53,则会发生以下情况:

    [NEW] udp      17 30 src=192.0.2.2 dst=198.51.100.1 sport=40037 dport=53 [UNREPLIED] src=127.0.0.1 dst=192.0.2.2 sport=53 dport=40037
    [NEW] udp      17 30 src=192.0.2.2 dst=192.0.2.2 sport=53 dport=40037 [UNREPLIED] src=172.16.0.22 dst=172.16.0.22 sport=40037 dport=53

回复与初始查询不相关(因为源 IP 不是 127.0.0.1),因此 conntrack 将此作为第二个流处理。同时,客户端将其 UDP 套接字置于连接模式,这意味着从错误的源 IP(即使是正确的端口)接收到的 UDP 数据包将被拒绝,并且服务器会收到 ICMP 错误(这可以通过 见证tcpdump -i lo)。

更正非常简单:不要使用MASQUERADEbut SNAT。当然,它现在必须专门针对此特定流(您不希望将SNAT所有内容都转换为 127.0.0.1),因此请MASQUERADE将此行替换为:

iptables -t nat -A POSTROUTING -p udp --dport 53 -j SNAT --to-source 127.0.0.1

通过更正后的流程,本地服务器现在使用 conntrack 的预期地址进行回复,该地址现在将其关联到之前的流程中并正确地对其进行 de-SNAT:

    [NEW] udp      17 30 src=192.0.2.2 dst=198.51.100.1 sport=38871 dport=53 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=53 dport=38871
 [UPDATE] udp      17 30 src=192.0.2.2 dst=198.51.100.1 sport=38871 dport=53 src=127.0.0.1 dst=127.0.0.1 sport=53 dport=38871

客户端收到预期的源 198.51.100.1 并且一切按预期工作。

传输控制协议不会遭受相同的结果,因为一旦在 192.0.2.2 和 127.0.0.1 之间建立连接,回复就会在同一个已建立的连接内,它不是像 UDP 那样的新连接,因此已经有了预期的源,并且是由 conntrack 正确处理。为了一致性,最好添加这个:

iptables -t nat -A POSTROUTING -p tcp --dport 53 -j SNAT --to-source 127.0.0.1

两个注意事项:

  • 对于您的具体情况,route_localnet不需要,因为所有数据包都是本地的并保留在lo.相反:转发发送到 127.0.0.1 的数据包将需要它(以及其他技巧)。

  • 如果您的 DNS 服务器也是向外部发送查询的 DNS 客户端(递归 DNS 服务器就是这种情况),或者它自己的查询将被重新路由到自身,从而形成循环,您可能需要额外的例外规则。通常通过让服务器以特定用户运行并使用 iptables 来解决-m owner匹配。就像在每组规则之前插入这样的内容(在nat/OUTPUT和中nat/POSTROUTING):

    iptables -t nat -I .... -m owner --uid-owner unbound -j RETURN
    

相关内容