nft 配置使本地 NATed FTP 服务器公开可用

nft 配置使本地 NATed FTP 服务器公开可用

一切都将在一个隔离的网络上,安全不是问题。eth0
连接到“公共”网络。地址由 DHCP 分配。eth1
连接到提供 ssh、telnet、“其他”和 ftp 的“专用网络”服务器。
在此示例中,此服务器将具有固定 IP(192.168.1.2)。

网关运行的是 debian buster 和 Linux 内核 4.19.94

nft 与 NAT 一起使用
这是我目前的“网关” nft 配置:

table ip my_nat {
    chain my_prerouting { type nat hook prerouting priority 0;
    tcp dport { 2222 } dnat to :22 # 2222 backdoor for ssh to the gateway
    tcp dport { 1-1023 } dnat to 192.168.1.2
  }
  chain my_postrouting {
    type nat hook postrouting priority 100;
    ip daddr 192.168.1.2  masquerade
  }
}

我需要添加什么才能使 FTP 正常工作

答案1

总结

# nft add ct helper ip my_nat ftp-incoming '{ type "ftp" protocol tcp; }'
# nft add chain ip my_nat my_helpers '{ type filter hook prerouting priority 10; }'
# nft add rule ip my_nat my_helpers iif eth0 ip daddr 192.168.1.2 tcp dport 21 ct helper set ftp-incoming
# modprobe nf_nat_ftp

下面有更多详细信息...

有问题的协议的协议帮助模块

FTP 是一种旧协议,对防火墙不太友好:FTP 命令通道 (21/TCP) 上的命令会协商一个临时端口,用于下一个传输命令。因此,状态防火墙必须监听这些命令并做出响应,以暂时预先允许使用合适的端口。

在 Linux 上,这是由协议特定的辅助模块提供的,这些模块是连接跟踪,Netfilter 子系统跟踪 NAT 和状态防火墙的连接。使用 FTP,当在 FTP 命令端口上看到下一次传输的端口协商(主要是 PORT、EPRT、PASV 或 EPSV)时,帮助程序会在特殊连接跟踪表(conntrack 期望表)将等待下一个相关的和预期的数据连接。

我的答案使用了现代“安全”处理方式,如下所述这个博客关于iptables对于一般情况和nftables维基百科man nft用于处理nftables不同于iptables

安全使用辅助程序和 nftables 规则

Linux 内核 4.7+(包括 4.19)默认使用安全方法:加载(此处为 FTP)辅助模块将不再使它监听所有具有 TCP 源或目标端口 21 的数据包,直到特定的nftables(或者iptables) 语句告诉它应该在什么(受限)情况下进行监听。这样可以避免不必要的 CPU 使用率,并且允许随时更改要监听的 FTP 端口,只需更改一些规则(或设置)。

第一部分是声明可以触发监听的流。在nftablesiptables。这里使用有状态对象声明ct helper,并且激活它的过滤器必须在之后完成连接跟踪(尽管iptables需要之前完成的操作)。man nft告诉:

与 iptables 不同,辅助分配需要在 conntrack 查找完成后执行,例如使用默认的 0 钩子优先级。

nft add ct helper ip my_nat ftp-incoming '{ type "ftp" protocol tcp; }'

nft add chain ip my_nat my_helpers '{ type filter hook prerouting priority 10; }'
nft add rule ip my_nat my_helpers iif eth0 ip daddr 192.168.1.2 tcp dport 21 ct helper set ftp-incoming

我选择了同一张表,但这可以在另一个表中创建,只要有状态对象声明和引用它的规则在同一个表中。

当然,你也可以选择限制较少的规则。用以下规则替换最后一条规则将产生与传统模式相同的效果:

nft add rule ip my_nat my_helpers tcp dport 21 ct helper set ftp-incoming

仅供参考,不再使用的传统模式不需要上述规则,而只需要这个切换(以及手动加载相关内核模块):

sysctl -w net.netfilter.nf_conntrack_helper=1

确保nf_nat_ftp已加载

内核模块nf_conntrack_ftp会自动加载由 创建的依赖项ct helper ... type "ftp"。 但 并非如此nf_nat_ftp,当在数据流端口上完成 NAT 时,还需要在命令 port 中启用数据包处理。

例如,为了在加载nf_nat_ftp时提取模块,可以向文件中添加以下内容:nf_conntrack_ftp/etc/modprobe.d/local-nat-ftp.conf

install nf_conntrack_ftp /sbin/modprobe --ignore-install nf_conntrack_ftp; /sbin/modprobe --ignore-install nf_nat_ftp

或者,只需添加例如/etc/modules-load.d/local-nat-ftp.conf

nf_nat_ftp

无论如何,现在应该执行这个命令以确保它已被加载:

modprobe nf_nat_ftp

关于防火墙

这是防火墙的附加说明。也可以使用带有某些限制的防火墙规则,而不是允许任何标记为有关的经过连接跟踪

例如,尽管 FTP 辅助模块可以处理被动和主动模式,但是如果出于某种原因,人们只想允许被动模式(从客户端到服务器的数据连接)而不是“主动” ftp(从服务器源端口 20 到客户端的数据连接),人们可以在规则集的防火墙部分使用例如这些规则,而不是通常的ct state established,related accept

ct state established accept
ct state related ct helper "ftp" iif eth0 oif eth1 tcp sport 1024-65535 accept
ct state related ct helper "ftp" drop
ct state related accept 

其他类型的有关的与 FTP 无关的流量保持接受(或可以进一步拆分)


帮助者处理的示例

这里(在模拟环境中)有两个连接跟踪测量的事件列表期待表格和连接跟踪表 OP 的规则 + 上面的附加规则,其中 Internet 客户端 203.0.113.101 使用路由器的公共 IP 地址 192.0.2.2 以被动模式执行 FTP,并在登录后使用 LIST 命令:

# conntrack -E expect
    [NEW] 300 proto=6 src=203.0.113.101 dst=192.0.2.2 sport=0 dport=37157 mask-src=0.0.0.0 mask-dst=0.0.0.0 sport=0 dport=65535 master-src=203.0.113.101 master-dst=192.0.2.2 sport=50774 dport=21 class=0 helper=ftp
[DESTROY] 300 proto=6 src=203.0.113.101 dst=192.0.2.2 sport=0 dport=37157 mask-src=0.0.0.0 mask-dst=0.0.0.0 sport=0 dport=65535 master-src=203.0.113.101 master-dst=192.0.2.2 sport=50774 dport=21 class=0 helper=ftp

同时地:

# conntrack -E
    [NEW] tcp      6 120 SYN_SENT src=203.0.113.101 dst=192.0.2.2 sport=50774 dport=21 [UNREPLIED] src=192.168.1.2 dst=192.168.1.1 sport=21 dport=50774 helper=ftp
 [UPDATE] tcp      6 60 SYN_RECV src=203.0.113.101 dst=192.0.2.2 sport=50774 dport=21 src=192.168.1.2 dst=192.168.1.1 sport=21 dport=50774 helper=ftp
 [UPDATE] tcp      6 432000 ESTABLISHED src=203.0.113.101 dst=192.0.2.2 sport=50774 dport=21 src=192.168.1.2 dst=192.168.1.1 sport=21 dport=50774 [ASSURED] helper=ftp
    [NEW] tcp      6 120 SYN_SENT src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 [UNREPLIED] src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835
 [UPDATE] tcp      6 60 SYN_RECV src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835
 [UPDATE] tcp      6 432000 ESTABLISHED src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]
 [UPDATE] tcp      6 120 FIN_WAIT src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]
 [UPDATE] tcp      6 30 LAST_ACK src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]
 [UPDATE] tcp      6 120 TIME_WAIT src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]

期望的开始proto=6 src=203.0.113.101 dst=192.0.2.2 sport=0 dport=37157表明下一个从 203.0.113.101:* 到 192.0.2.2:37157 的 TCP 连接将关联(状态有关的) 与 FTP 连接。虽然无法直接看到,但由于还加载了 NAT FTP 辅助模块,因此服务器响应 PASV/EPSV 命令发送的实际数据被拦截并转换,因此客户端连接到 192.0.2.2 而不是 192.168.1.2,这当然会在客户端上失败。

尽管第二股流(第二新的线)没有明确的规则我的预路由,它已成功 DNAT 到路由器后面的服务器。


笔记

  • 如果 FTP 控制端口已加密(AUTH TLS...),则辅助模块将无法再监听协商端口,这将无法正常工作。必须在 FTP 服务器配置和防火墙/NAT 路由器上配置保留的端口范围,并配置服务器,使其在协商时发送正确的(公共)IP 地址而不是自己的 IP 地址。如果服务器没有到 Internet 的路由(这里的情况似乎如此),则无法使用主动 FTP 模式。

  • 吹毛求疵:预路由优先级 10 确保即使对于低于 4.18 的内核,新流也已经发生 NAT(OP 选择钩子预路由优先级 0 而不是通常的 -100,因为这在 nftables 中很少重要),因此daddr 192.168.1.2可以使用。如果优先级为 0(或低于 0),则可能(未经验证)规则会看到第一个数据包仍未进行 NAT,具有公共 IP 目标地址,但会捕获同一流的后续数据包,因为它们直接由连接跟踪优先级为 -200。最好保持安全并使用 10。实际上,从内核 4.18 开始,这已不相关(请参阅犯罪参考待修复补丁)其中 NAT 优先级仅与多个 nat 链之间的比较有关(并允许在 iptables 遗留系统和 nftables 中混合 NAT)。

答案2

经过反复尝试,我得出了以下 nftables.conf。它按预期工作,支持双向 NAT,并将服务器上除一个端口之外的所有端口导出到“公共”网络。端口 2222 仍用作网关的“后门”,如果我需要再次访问它,就会使用它 :-)

table ip my_nat {
        ct helper ftp-incoming {
                type "ftp" protocol tcp
                l3proto ip
        }

        chain my_prerouting {
                type nat hook prerouting priority 0; policy accept;
                iifname "eth0" tcp dport { 2222 } dnat to :ssh
                iifname "eth0" tcp dport { 1-2221, 2223-65535 } dnat to 192.168.0.2
        }

        chain my_postrouting {
                type nat hook postrouting priority 100; policy accept;
                ip daddr 192.168.0.2 masquerade
                oifname "eth0" masquerade
        }

        chain my_helpers {
                type filter hook prerouting priority 10; policy accept;
                iif "eth0" ip daddr 192.168.0.2 tcp dport ftp ct helper set "ftp-incoming"
        }

}

相关内容