从 LAN 内部访问经过 DNAT 的 Web 服务器

从 LAN 内部访问经过 DNAT 的 Web 服务器

我有一个带有路由器的小型网络,它维持与互联网、服务器和本地网络中的一些工作站的连接。

网络图

服务器旨在从互联网访问,并且路由器 iptables 中设置了几个 DNAT 条目,如下所示:

-A PREROUTING -i ppp0 -p tcp -m multiport --dports 22,25,80,443 -j DNAT --to-destination 192.168.2.10

外部数据包通过接口到达路由器ppp0,内部数据包从 发出br-lan,实际上包括交换机和 WLAN 适配器。问题是,虽然外部访问工作正常,但尝试通过 DNS 解析的外部 IP(分配给ppp0)从 LAN 内部访问服务器会失败。

我能想到的唯一解决方案是向/etc/hosts指向内部 IP 的路由器添加静态条目,但由于没有通配符(并且我至少为该系统分配了三个顶级域,还不包括数十个子域),所以这种方法相当棘手且容易失败。您能建议一些更好的方法吗?

我只发现了这个问题,这没有什么帮助。

如果相关的话,路由器将运行带有 dnsmasq 的 OpenWRT 10.03 Kamikaze。

答案1

令我惊讶的是,近 8 年后,没有人解释如何使用 OpenWRT 中默认使用的 UCI 配置系统以正确的方式执行此操作。

Steven Monday 的回答是正确的,但它直接使用iptables命令,这比 UCI 配置系统低一层,如果可能的话,大多数 OpenWRT 用户最好不要触碰它。

在 UCI 中,从另一台内部主机通过公共 IP/端口组合访问内部服务器的正确方法是启用reflection文件中每个特定 DNAT 目标下的配置选项/etc/config/firewall。此行为已记录这里

例如:

config redirect option target 'DNAT' option src 'wan' option dest 'lan' option proto 'tcp' option src_dport '44322' option dest_ip '192.168.5.22' option dest_port '443' option name 'apache HTTPS server' option reflection '1'

注意:根据 OpenWRT 文档,reflection默认情况下启用。在我的测试中,情况并非如此。

答案2

我删除了原来的答案,因为我并不完全相信它是正确的。从那时起,我就花了一些时间设置了一个小型虚拟机虚拟网络来模拟有问题的网络。以下是一组对我有用的防火墙规则(格式iptables-savenat仅用于表格):

-A PREROUTING -d 89.179.245.232/32 -p tcp -m multiport --dports 22,25,80,443 -j DNAT --to-destination 192.168.2.10
-A POSTROUTING -s 192.168.2.0/24 -o ppp0 -j MASQUERADE
-A POSTROUTING -s 192.168.2.0/24 -d 192.168.2.10/32 -p tcp -m multiport --dports 22,25,80,443 -j MASQUERADE

第一条POSTROUTING规则是与 LAN 共享互联网连接的直接方法。我将其留在那里是为了完整性。

PREROUTING规则和第二条POSTROUTING规则共同建立了适当的 NAT,这样无论连接来自 LAN 外部还是内部,都可以通过外部 IP 地址连接到服务器。当 LAN 上的客户端通过外部 IP 地址连接到服务器时,服务器会将这些连接视为来自路由器的内部 IP 地址 (192.168.2.1)。

有趣的是,事实证明,第二条 POSTROUTING 规则的几个变体也有效。如果将目标更改为-j SNAT --to-source 192.168.2.1,效果(毫不奇怪)与 相同MASQUERADE:服务器将来自本地 LAN 客户端的连接视为源自路由器的内部的IP 地址。另一方面,如果将目标更改为-j SNAT --to-source 89.179.245.232,则 NAT 仍然有效,但这次服务器会将来自本地 LAN 客户端的连接视为来自路由器的外部的IP地址(89.179.245.232)。

最后,请注意,您原来的PREROUTING/DNAT规则-i ppp0不起作用,因为该规则从不匹配来自 LAN 客户端的数据包(因为这些数据包不会通过接口进入路由器ppp0)。可以通过添加PREROUTING仅针对内部 LAN 客户端的第二条规则使其工作,但这不太优雅(在我看来),并且仍然需要明确引用外部 IP 地址。

现在,即使在详细阐述了“发夹式 NAT”(或“NAT 环回”或“NAT 反射”,无论人们喜欢如何称呼它)解决方案之后,我仍然认为,采用分割水平 DNS 解决方案(外部客户端解析为外部 IP,内部客户端解析为内部 IP)是更明智的选择。为什么?因为了解 DNS 工作原理的人比了解 NAT 工作原理的人多,而构建良好系统的一个重要部分是选择使用可维护的部分。与晦涩难懂的 NAT 设置相比,DNS 设置更容易被理解,从而更容易被正确维护(当然,在我看来)。

答案3

一种常见的解决方案是将您的内部主机指向本地 DNS 服务器,该服务器返回这些主机名的正确“内部”地址。

另一个解决方案(我在 Cisco 防火墙上工作时就使用了此解决方案)是重写防火墙上与这些地址对应的 DNS 响应。我认为目前没有适用于 Linux 的工具可以做到这一点。

您应该能够配置网关上的路由以执行正确的操作。您可能需要配置服务器以了解其外部映射的 IP 地址(例如,通过将其分配给虚拟接口)。使用此配置,从一个内部系统到另一个内部系统的通信(使用它的“外部”地址)将通过路由器。

答案4

对于那些希望使用 nftables(iptables 的官方替代品)来做到这一点的人,我想出了以下方法:

define dnat_targets = {
    http : 10.0.10.1 . http, 
    https : 10.0.10.1 . https,
    32400 : 10.0.10.4 . 32400, 
    25565 : 10.0.10.8 . 25565, 
}

define dnat_allowed = {
    10.0.10.1 . http,
    10.0.10.1 . https,
    10.0.10.4 . 32400, 
}

table inet nat {
    map dnat_destinations {
        type inet_service : ipv4_addr . inet_service
        elements = $dnat_targets
    }

    set dnat_masq {
        type ipv4_addr . inet_service
        elements = $dnat_allowed
    }

    chain prerouting {
        ip daddr != 10.0.0.0/8 fib daddr type local dnat ip addr . port to tcp dport map @dnat_destinations
    }

    chain postrouting {
        ip saddr 10.0.0.0/8 ip daddr . tcp dport @dnat_masq masquerade
    }
}

table inet filter {
    set dnat_allowed {
        type ipv4_addr . inet_service
        elements = $dnat_allowed
    }

    chain forward {
        ip daddr . tcp dport @dnat_allowed accept
    }
}

请注意,这是我的防火墙定义的一个片段,您需要在使用它之前创建链。它也与原始帖子的情况不完全匹配,需要调整 IP 地址和 RFC1918 范围。

要添加 DNAT 目标,您只需将它们添加到定义中dnat_targets,然后允许集合中的目标dnat_allowed。据我所知,您不能只使用dnat_targets集合来做到这一点,但这样做的一个优点是,您可以通过从集合中删除它们来打开和关闭 dnat 规则,dnat_allowed同时仍保留映射以备日后需要。

关于这一点的真正好处是,地图是 nftables 中的一流构造,换句话说,无论您有多少个 DNAT 目标(1,10,100,1000),数据包只需要遍历使用地图和集合的这三个规则(类似于 ipset 的工作方式)。

我添加的另一件事是使用未知的公共 IP 地址使其工作,这样当 wan 接口使用 DHCP 时您可以安全地使用它,并且您仍然可以在路由器盒上运行 LAN 可访问的网站或其他任何东西。

最后,这没有显示完全发挥作用所需的 WAN 连接的伪装规则。

逐一介绍规则:

ip daddr != 10.0.0.0/8 fib daddr type local dnat ip addr . port to tcp dport map @dnat_destinations

这是主要的 DNAT 规则,如果目标地址不在 10.0.0.0/8 中,并且是当地的地址(定义为分配给本地机器接口的任何地址),然后我们将对其进行 DNAT。这可以确保您仅匹配发往未知 WAN 地址的数据包。请注意,如果您跳过,daddr != 10.0.0.0/8则 dnat 规则将匹配发往路由器上任何接口的请求,因此当通过 10.0.0.1:80 访问时,管理员 Web UI 将被 dnated 而不是显示,如果您跳过,则fib daddr type local使用地图中任何端口的任何请求(例如任何网站)都将被 dnated(例如,如果您尝试在其中一个客户端上访问 google.com,您最终会进入您尝试托管的网站)。

我的路由器有多个局域网地址,如果你的路由器只有一个局域网地址,你可以进一步简化规则。DNAT 是使用目标端口上的地图查找来定义的。

ip saddr 10.0.0.0/8 ip daddr . tcp dport @dnat_masq masquerade

这类似于 iptables 答案中的伪装规则,如果源是 LAN 地址,并且目标地址和端口在集合中dnat_allowed,则伪装。顺便说一句,这样做的原因是,如果您不更改源地址,那么当服务器响应 Web 请求时,它将看到 LAN 地址,然后 arp“谁拥有”该 LAN 地址,您的机器将响应该 arp 请求,然后它将直接将数据包发送到您的本地机器。但是,您的本地机器向路由器的公共 IP 发出请求,因此当它直接从服务器获得响应时,它不知道如何处理它,因为它没有期待来自该地址的数据包,因此它将其标记为无效并丢弃它。通过在这里伪装,您可以强制 Web 服务器回复路由器,然后路由器使用 conntrack 将其发送回您的机器。

ip daddr . tcp dport @dnat_allowed accept

最后,转发过滤规则,如果目标地址和端口在dnat_allowed设定中,则接受该数据包。

相关内容