这是有关网络地址转换(NAT)的系列文章中的第一篇。本节将展示如何使用iptables/nftables数据包跟踪功能来解决与NAT相关的连接问题。简介网络地址转换(NAT)是一种将容器或虚拟机暴露给Internet的方法。传入的连接请求将其目标地址重写为另一个地址,然后路由到容器或虚拟机。同样的技术也可以用于负载平衡,其中传入的连接被分配到不同的服务器。当NAT未按预期工作时,连接请求将失败,将暴露错误的服务,连接将在错误的容器中结束,或者请求将超时等。调试此类问题的一种方法是检查传入请求匹配预期或配置的转换。连接跟踪NAT不仅仅是修改IP地址或端口号。例如,当将地址X映射到Y时,不需要添加新的规则来执行反向转换。一个名为“conntrack”的netfilter系统识别对已建立连接的回复。每个连接在conntrack系统中都有自己的NAT状态。反向转换是自动完成的。规则匹配跟踪nftables工具(在较小程度上,iptables)允许检查数据包是如何处理的,以及数据包匹配的规则集中的规则。要使用此特殊功能,可以在适当的位置插入“跟踪规则”。这些规则选择要跟踪的数据包。假设来自IP地址C的主机正在访问IP地址S和端口P的服务。我们想知道数据包匹配的NAT转换规则,系统检查的规则以及数据包被丢弃的位置。由于我们正在处理传入连接,因此我们将规则添加到预路由挂钩中。预路由意味着内核尚未决定将数据包发送到哪里。更改目标地址通常会导致数据包由系统转发,而不是由主机自己处理。初始配置#nft'addtableinettrace_debug'#nft'addchaininettrace_debugtrace_pre{typefilterhookpreroutingpriority-200000;}'#nft"insertruleinettrace_debugtrace_preipsaddr$Cipdaddr$Stcpdport$Ptcpflagssynlimitrate1/secondmetanftraceset1"第一条规则添加新的规则表,方便删除和未来的调试规则。nftdeletetableinettrace_debug命令可以删除调试时临时添加到表中的所有规则和链。第二条规则在系统执行路由之前创建一个基本钩子(preroutinghook),并将其优先级设置为负数,以确保它在连接跟踪过程和NAT规则匹配之前执行。然而,最重要的部分是第三条规则的最后一段:metanftraceset1。该规则将导致系统记录与匹配该规则的数据包相关的所有事件。要尽可能高效地查看跟踪(提高信噪比),请考虑为跟踪的事件添加速率限制以保持数量易于管理。一个好的选择是限制最多每秒一条消息或每分钟最多一条消息。上面的例子记录了从终端$C到终端$S的端口$P的所有SYN包和SYN/ACK包。限速配置语句可以防止事件过多导致的泛洪风险。事实上,在大多数情况下,只记录一条消息就足够了。对于iptables用户,配置过程类似。等效的配置规则类似于:#iptables-traw-IPREROUTING-s$C-d$S-ptcp--tcp-flagsSYNSYN--dport$P-mlimit--limit1/s-jTRACE获取trace事件nft原生工具用户可以直接运行nft进入nfttrace模式:#nftmonitortrace这条命令会打印出接收到的消息以及匹配消息的所有规则(使用CTRL-C停止输出):traceidf0f627iprawpreroutingpacket:iif"veth0"ethersaddr..我们将在下一章详细分析这个结果。如果您使用的是iptables,请首先使用iptables--version命令检查安装的版本。例如:#iptables--versioniptablesv1.8.5(legacy)(legacy)表示跟踪的事件会记录在内核的环形缓冲区中。您可以使用dmesg或journalctl命令查看这些事件。这些调试输出缺少一些信息,但在概念上类似于新工具提供的输出。您需要先查看记录规则的行号,然后手动将其与活动的iptables规则集相关联。如果输出显示(nf_tables),可以使用xtables-monitor工具:#xtables-monitor--trace如果上面的命令只显示版本号,还需要看dmesg/journalctl的输出。xtables-monitor工具和nft监控跟踪工具使用相同的内核接口。它们之间的唯一区别是xtables-monitor工具将使用iptables语法打印事件,如果同时使用iptables-nft和nft,它不会打印那些使用maps/sets或其他只有nftables支持的函数规则。示例假设我们需要调试虚拟机/容器的端口无法访问的问题。ssh-p122210.1.2.3命令应该能够远程连接到该服务器上的容器,但连接请求超时。您在运行该容器的主机上具有登录权限。现在登录机器并添加跟踪规则。通过前面的案例,您可以看到如何添加一个临时的调试规则表。trace规则类似这样:nft"insertruleinettrace_debugtrace_preipdaddr10.1.2.3tcpdport1222tcpflagssynlimitrate6/minutemetanftraceset1"添加以上规则后,以trace模式运行nftmonitortraceStartnft,然后重试刚刚失败的ssh命令。如果规则集很大,就会有很多输出。不要担心输出,我们将在下一节中逐行分析。跟踪ID9c01f8inettrace_debugtrace_pre数据包:iif“enp0”ethersaddr..ipsaddr10.2.1.2ipdaddr10.1.2.3ip协议tcptcpdport1222tcp标志==syntraceid9c01f8inettrace_debugtrace_pre规则ipdaddr10.2tcpdportdportdportdport1222tcp标志syn限制速率6/分钟元nftrace集1(判断继续)跟踪id9c01f8inettrace_debugtrace_preverdictcontinuetraceid9c01f8inettrace_debugtrace_pre策略accepttraceid9c01f8inetnat预路由数据包:iif“enp0”ethersaddr..ipsaddr10.2.1.2ipdaddr10.1.2.3ipprotocoltcptcpdport1222tcpflags==syntraceid9c01f8inetnatpreroutingruleipdaddr10.1.2.3tcpdport1222dnatipto192.168.70.10:22(verdictetfilterforward8in9c01f8)数据包:iif"enp0"oif"veth21"ethersaddr..ipdaddr192.168.70.10..tcpdport22tcpflags==syntcpwindow29200traceid9c01f8inetfilterforwardrulectstatusdnatjumpallowed_dnats(verdictjumpallowed_dnats)traceid9c01f8inetfilterallowed_dnatsruledrop(verdictdrop)traceid20a4efinettrace_debugtrace_prepacket:iif"enp0"ethersaddr..ipsaddr10.2.1.2ipdaddr.2.ip1port3pro10。tcpflags==syn逐行分析跟踪结果。输出结果的第一行是触发后续输出的消息编号。该行的语法与nft规则相同,同样包含了接收消息的头域信息。您还可以在此行上找到接收数据包的接口名称(此处为enp0)、数据包的源和目标MAC地址、数据包的源IP地址(可能很重要-报告问题的人可能选择了错误的或意外的主机),以及TCP源端口和目标端口。您还会在该行的开头看到一个“跟踪号”。此编号标识匹配跟踪规则的特定数据包。第二行包含数据包匹配的第一条跟踪规则:traceid9c01f8inettrace_debugtrace_preruleipdaddr10.2.1.2tcpdport1222tcpflagssynlimitrate6/minutemetanftraceset1(verdictcontinue)添加跟踪规则。此处显示的第一条规则始终是激活数据包跟踪的规则。如果在此之前还有其他规则,则不会在此处显示。如果没有trace输出结果,说明没有达到这个trace规则,或者没有匹配成功。下面两行表示没有后续的匹配规则,trace_prehook让数据包继续传输(判定为accepted)。下一个匹配规则是:traceid9c01f8inetnatpreroutingruleipdaddr10.1.2.3tcpdport1222dnatipto192.168.70.10:22(verdictaccept)此DNAT规则设置到其他地址和端口的映射。规则中的参数192.168.70.10是需要接收数据包的虚拟机地址,目前为止没有问题。如果它不是正确的虚拟机地址,则地址输入不正确,或者它匹配了错误的NAT规则。IP转发我们可以从下面的输出中看到,IP路由引擎告诉IP协议栈数据包需要转发到另一台主机:traceid9c01f8inetfilterforwardpacket:iif"enp0"oif"veth21"ethersaddr..ipdaddr192.168.70.10..tcpdport22tcpflags==syntcpwindow29200这是收到消息的另一种表示,但有一些有趣的区别。结果现在有一组输出接口。这在之前是不存在的,因为预路由规则在路由决策之前(预路由挂钩)。trackingnumber和之前一样,所以还是同一条消息,只是修改了目的地址和端口。假设还有匹配tcpdport1222的规则,他们不会对现阶段的数据包产生任何影响。如果该行不包含输出接口(oif),则表示路由决策已将数据包路由到本地机器。调试路由过程是另外一个话题,本文不做介绍。traceid9c01f8inetfilterforwardrulectstatusdnatjumpallowed_dnats(verdictjumpallowed_dnats)输出表明数据包匹配跳转到allowed_dnats链的规则。下一行显示了连接失败的根本原因:traceid9c01f8inetfilterallowed_dnatsruledrop(verdictdrop)该规则无条件丢弃该数据包,因此后续没有关于该数据包的日志输出。下一行是另一个数据包的输出:traceid20a4efinettrace_debugtrace_prepacket:iif"enp0"ethersaddr..ipsaddr10.2.1.2ipdaddr10.1.2.3ipprotocoltcptcpdport1222tcpflags==synThetrackingnumber和以前不一样了,但是消息的内容和以前一样。这是一次重传尝试:第一个数据包被丢弃,因此TCP尝试重传。其余输出可以忽略,因为它不提供新信息。现在是检查链条的时候了。规则集分析在上一节中,我们发现在inetfilter表中一个名为allowed_dnats的链中丢弃了数据包。现在让我们检查一下:#nftlistchaininetfilterallowed_dnatstableinetfilter{chainallowed_dnats{metanfprotoipv4ipdaddr.tcpdport@allow_inacceptdrop}}接受设置了@allow_in的数据包的规则不会显示在跟踪日志中。我们通过列出元素再次检查上述消息的目标地址是否在@allow_in集合中:#nft"getelementinetfilterallow_in{192.168.70.10.22}"Error:Couldnotprocessrule:Nosuchfileordirectory正如预期的那样,地址-服务对没有出现在集合中。我们将其添加到集合中。#nft"addelementinetfilterallow_in{192.168.70.10.22}"现在运行查询命令,它将返回新添加的元素。#nft"getelementinetfilterallow_in{192.168.70.10.22}"tableinetfilter{setallow_in{typeipv4_addr.inet_service元素={192.168.70.10。22}}}ssh命令现在应该工作并且跟踪结果反映出该改变:traceid497abf58inetfilterforwardrulectstatusdnatjumpallowed_dnats(verdictjumpallowed_dnats)traceid497abf58inetfilterallowed_dnatsrulemetanfprotoipv4ipdaddr。tcpdport@allow_inaccept(verdictaccept)traceid497abf58ippostroutingpacket:iif"enp0"oif"veth21"ether..traceid497abf58ippostroutingpolicyaccept这表明数据包通过了转发路径中的最后一个钩子-postrouting。如果现在仍然无法连接,问题可能出在消息流的后期,这可能不在nftables规则集的范围内。总结本文介绍了如何通过nftables的追踪机制来检查丢包或其他类型的连接问题。本系列的下一篇文章将介绍如何检查可能与连接跟踪流相关的连接跟踪系统和NAT信息。
