虚拟主机FTP被动模式下返回localhost IP
前言
准备将备案后的博客放到阿里虚拟主机上,但是无奈 hexo 官方的 ftp 部署插件年久失修,自己有没有修复能力,所以只好自己写脚本上传了,调试程序时发现了很诡异的一个问题。
问题症状
先贴调试脚本,使用被动模式,众所周知在现在 NAT 满天飞的家庭宽带中主动模式基本不可用:
1 | from ftplib import FTP |
问题症状:
在本机上执行脚本,返回日志如下:
1 | *cmd* 'USER xxxx' |
在虚拟机中执行脚本,输出如下:
1 | *cmd* 'USER xxxx' |
问题出在 PASV
命令的回复上:
*resp* '227 Entering Passive Mode(127,0,0,1,156,69).'
*resp* '227 Entering Passive Mode(116,62,0,0,156,73).'
两次服务器返回的高位端口没什么问题, 但是 IP 地址是不一样的,一个是正常的公网 IP,一个是 localhost 环回地址,这就导致我在本机执行脚本时无法连接到服务器返回给我的 IP 地址(环回地址)上,最后连接失败!
本着控制变量的原则,我将测试脚本放到虚拟机中,然后将虚拟机的网络模式由 NAT 模式改为 bridge 模式,发现运行结果和本机一致,虚拟机的环境没有任何更改,只是将网络模式更改了,也就是说将虚拟机的内网 IP 由原来的 192.168.233.x
网段更改为了 192.168.1.x
网段,同样的脚本虚拟主机的 FTP 服务器返回给我的结果是不一样的!这就直接导致了我无法通过被动模式和 FTP 服务器进行连接。
注:filezilla 这类 ftp 客户端在被动模式下貌似都有一个设置,就是会自动替换服务器返回的错误的 IP,但是自己写的脚本没有这个功能,所以会出现问题。
本地抓包分析
在虚拟机中抓包结果如下,数据包看起来没有任何问题,仅仅只是源 IP 不一样。
服务器抓包分析
为了看一下服务器端收到的数据是什么样的,我在自己的服务器上搭建了一个简单的 ftp server,然后使用 tcpdump 抓包,结果如下:
追踪 TCP 流数据如下:
服务器仍然返回的是内网地址,看起来服务器并没有收到任何关于我内网的任何信息,按理说不可能根据我的内网信息返回不同的 IP 地址。
验证
到这里,我有一个疑问,在虚拟机的网络模式转换过程中,网络模式的转换导致了在数据包到达服务器前多了一层 NAT,仅此而已,这应该不影响 ftp 数据包的抓发。
注:在验证这个问题之前走了许多弯路,前前后后大概花了将近两天的时间在里面。包括但不限于和阿里人员使用工单进行沟通,自己搭建服务器进行单向双向抓包等等。
但是为了确认虚拟机的 NAT 不影响 ftp 数据包的转发,我在虚拟机和本机中同时抓包验证了一下(事实证明这里还真有影响)。
在虚拟机中抓包的数据包如下:
在本机中抓到的包如下:
真相大白了,虚拟机的 NAT 功能的确替换了 ftp 服务器返回的错误的 IP 地址,但是不知道为什么会这样。
翻了一下 VMware 的知识库,找到了这个,但是没有看明白到底是不是支持 ftp 的 IP 地址自动替换功能,在另外一篇文章中找到解释。
中国的ip地址资源太少,很多情况下ftp会话两端要经过层层NAT、网关、防火墙。 导致PORT PASV中的ip 信息不一定都是正确的。比如client的ip是192.168.0.107,其实它是通过NAT连接上网的,该NAT对外的internet地址是218.2.135.1,那么client 发送的PORT指令中携带的192,168,0,107,xx,yy 对于server是没有意义的。 所幸现在的 NAT、防火墙一般都有FTP应用层感知能力,它能够截获ftp会话中的PORT PASSIVE, 自动将private的ip翻译成对外的正确的ip,并实时的在NAT上开放临时端口转发 。刚才的例子就会翻译成 218,2,135,1,xx,yy。 所以一般情况下,ftp还是能够正常工作的。
所以说这个问题可以解释为我的路由器自带的 NAT 没有 ftp 应用自动感知功能,所以导致了本机无法获取到正确的服务器 IP 地址,而 VMware 的 NAT 带有自动翻译 IP 地址功能,将服务器返回的错误的 IP 自动替换成了正确的服务器 IP,导致了这个诡异问题的出现。
解决方案
问题的原因搞清楚了,这里贴一下解决方案吧。
既然服务器返回的是错误的 IP,那么我们将它替换掉就好了,ftplib 的源码也不多,很简单就可以读懂,我们创建一个类,然后重写原来解析 PASV
命令的函数,让它返回服务器的真实 IP 就好了,代码如下:
1 | from ftplib import FTP, parse227, parse229 |
使用上面的代码就可以在任何环境下正常的连接 ftp 服务器了,因为我们直接忽略了服务器返回的 IP,使用服务器的真实外网 IP 替换了~
总结
看来以后遇到问题还是要多多动手验证,才能更快的发现问题在哪,还有尽量不留疑点,不能盲目相信其他软件🤣,因为你无法保证它会在后台做什么奇奇怪怪的事~。
抓包分析是解决问题的最快的解决方案,单向抓包不可以的话就双向抓包,如果再不行那肯定是方向错了~
本文章首发于个人博客 LLLibra146’s blog
本文作者:LLLibra146
更多文章请关注:
版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!
本文链接:
https://blog.d77.xyz/archives/586b93bb.html