2

我有一个用于测试的 Docker Compose 系统,我在其中对单页 Web 应用程序进行端到端测试。网站中的几个按钮将导致在一个容器 ( missive-transmitter) 中启动 FTP 连接,然后转到另一个容器 ( missive-testbox) 中的测试 FTP 服务器。

我在 PHP 中的 FTP 逻辑总是使用“被动”模式,我认为这是导致问题的原因。我创建了一个脚本来运行missive-transmitter,它是真实事物的简化版本。如下,直接从控制台运行:

<?php
# ftptest.php

error_reporting(-1);
ini_set('display_errors', true);

$conn = ftp_connect('missive-testbox', 21);

$ok1 = ftp_login($conn, 'missive_test', 'password');
if (!$ok1)
{
    die("Cannot log in\n");
}

// *** Start problem section
$ok2 = ftp_pasv($conn, true);
if (!$ok2)
{
    die("Cannot switch to passive mode\n");
}
// *** End problem section

$info = ftp_systype($conn);
echo "Info: $info\n";

$ok3 = ftp_put($conn, 'ftptest.php', 'ftptest.php', FTP_ASCII);
if (!$ok3)
{
    die("Cannot send a file\n");
}

现在,如果我删除该***部分(启用被动模式),那么脚本将起作用。如果我把它留在里面,我会得到这个:

信息:UNIX

警告:ftp_put(): php_connect_nonb() failed: Operation in progress (115) in /root/src/ftptest.php on line 23

警告:ftp_put(): TYPE 现在是 ASCII 在 /root/src/ftptest.php 的第 23 行

无法发送文件

我希望我的 FTP 操作在 PASV 模式下工作。

奇怪的是,如果我安装了一个 FTP 客户端,那么它似乎可以在主动或被动模式下工作,这是我不明白的。missive-transmitter侧面:

~/src $ # This is the `sh` shell in `missive-transmitter`
~/src $ #
~/src $ # Install LFTP in Alpine environment
~/src $ apk add lftp
~/src $ lftp missive_test@missive-testbox
Password: 
lftp missive_test@missive-testbox:~> set ftp:passive-mode off         
lftp missive_test@missive-testbox:~> put ftptest.php       
457 bytes transferred                            
lftp missive_test@missive-testbox:/> set ftp:passive-mode on 
lftp missive_test@missive-testbox:/> put ftptest.php        
457 bytes transferred
lftp missive_test@missive-testbox:/> 

PHP 是在做不同的事情,还是我实际上没有在控制台客户端中使用 PASV 模式?

我已经确认两个容器可以ping从各自的sh控制台相互连接。它们位于同一个(自定义)Docker 网络上。

missive-testboxDocker容器是基于的,gists/pure-ftpd所以据我所知应该配置正确。

更新

以下答案中的一个有用点是关于 NAT 可能如何使一侧使用错误的 IP 地址建立连接。但是,虽然我不是网络专家,但 IP 地址似乎在同一个子网上。

来自missive-transmitter

~ # ping missive-testbox
PING missive-testbox (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.076 ms

missive-testbox

~ # ping missive-transmitter
PING missive-transmitter (172.19.0.4): 56 data bytes
64 bytes from 172.19.0.4: seq=0 ttl=64 time=0.119 ms

认为他们都是172.19.0.x地址的事实意味着他们应该能够完全看到对方,尽管我愿意对这个假设进行更正。

更新 2

有人建议获取一些 FTP 客户端或服务器日志是调试此问题的好方法。客户端很简单。这里是与上面相同的操作,但在 LFTP 的调试模式下。

主动模式为先:

~/src # lftp -d missive_test@missive-testbox
Password: 
---- Resolving host address...
---- 1 address found: 172.19.0.2
lftp missive_test@missive-testbox:~> set ftp:passive-mode off
lftp missive_test@missive-testbox:~> put ftptest.php
---- Connecting to missive-testbox (172.19.0.2) port 21
<--- 220-Welcome to Pure-FTPd.
<--- 220-You are user number 1 of 5 allowed.
<--- 220-Local time is now 17:54. Server port: 21.
<--- 220-This is a private system - No anonymous login
<--- 220-IPv6 connections are also welcome on this server.
<--- 220 You will be disconnected after 15 minutes of inactivity.
---> FEAT
<--- 530 You aren't logged in
---> AUTH TLS
<--- 500 This security scheme is not implemented
---> USER missive_test
<--- 331 User missive_test OK. Password required
---> PASS XXXX
<--- 230 OK. Current directory is /              
---> FEAT
<--- 500 Unknown command
---> PWD
<--- 257 "/" is your current location
---> TYPE I
<--- 200 TYPE is now 8-bit binary
---> PORT 172,19,0,4,159,62
<--- 200 PORT command successful
---> ALLO 457
<--- 500 Unknown command
---> STOR ftptest.php
---- Accepted data connection from (172.19.0.2) port 20
<--- 150 Connecting to port 40766
---- Closing data socket
<--- 226-File successfully transferred
<--- 226 0.000 seconds (measured here), 3.16 Mbytes per second
---> SITE UTIME 20171030154823 ftptest.php
<--- 500 Unknown command
---> SITE UTIME ftptest.php 20171030154823 20171030154823 20171030154823 UTC
<--- 500 Unknown command
457 bytes transferred

好的,这很成功。这是LFTP中的被动版本,再次成功。

我注意到一开始的警告,关于需要修复的地址 - 这可能是相关的吗?如果任何一方将自己宣传为“本地主机”,这可能是一个问题:-)

lftp missive_test@missive-testbox:/> set ftp:passive-mode on 
lftp missive_test@missive-testbox:/> put ftptest.php        
---> PASV
<--- 227 Entering Passive Mode (127,0,0,1,117,54)
---- Address returned by PASV seemed to be incorrect and has been fixed
---- Connecting data socket to (172.19.0.2) port 30006
---- Data connection established
---> STOR ftptest.php
<--- 150 Accepted data connection
---- Closing data socket
<--- 226-File successfully transferred
<--- 226 0.000 seconds (measured here), 1.79 Mbytes per second
457 bytes transferred
4

2 回答 2

2

很难说这里做了哪些 FTP 操作。但可能是 PHP 正在使用PASV而 lftp 正在使用EPSV来设置被动模式。

如果PASV服务器同时发送 IP 地址和端口号,它将等待连接。与EPSV服务器相反,仅提供端口号,目标 IP 地址是来自当前 FTP 控制连接的地址。如果涉及 NAT(网络地址转换)(这在 Docker 设置中并非不可能),则服务器可能会看到与从 FTP 客户端外部可见的 IP 地址不同的内部 IP 地址,这意味着客户端无法连接到PASV命令响应中给出的(错误的)IP 地址。由于EPSV客户端不使用服务器提供的 IP 地址作为目标,因此不存在此问题。

于 2017-10-30T17:28:00.657 回答
0

有用的答案和评论的组合,加上来自 LFTP 的警告,使我找到了解决方案。PASV正如 Steffen 推测的那样,这个问题与模式中的 IP 可见性有关。LFTP 客户端有效地检测到我的服务器配置错误,因此透明地修复了它,这就是产生混淆的地方。

值得注意的是,PHP 不是那么聪明或善良——它没有这样的修复(当然,这在技术上是正确的)。

该配置的原因是默认的 Dockerfile 设置

ENV PUBLIC_HOST localhost

所以,我现在所做的就是在我自己的 Dockerfile 中用 LAN IP 替换它:

ENV PUBLIC_HOST 172.19.0.2

一次重新启动后,修复消息从 LFTP 中消失,我的 PHP 脚本工作。

更新

由于不知道能不能靠上面IP地址的静态性,所以我把它重置为容器的名字:

ENV PUBLIC_HOST missive-testbox

这似乎工作正常。

于 2017-10-30T18:31:47.673 回答