You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pyftpdlib搭建的FTP服务器无法从公网访问的问题排查

公网无法访问Docker中FTP服务器的问题排查与解决

问题场景

使用pyftpdlib搭建FTP服务器并运行在Docker容器中,本地访问完全正常,但通过公网IP连接时触发ConnectionRefusedError,且服务器端无任何日志记录。

服务器代码

from pyftpdlib import servers, handlers
address = ("0.0.0.0", 21)
server = servers.FTPServer(address, handlers.FTPHandler)
server.banner = "Welcome to My FTP Server"
handler = server.handler
handler.authorizer.add_user("username", "password", ".", perm="elr")
handler.authorizer.add_anonymous(".", perm="elr")
handler.masquerade_address = '97.117.28.178' # from `curl ifconfig.me`
handler.passive_ports = range(60000, 60100)  
server.serve_forever()

Docker启动命令

docker run --rm -it -p 20:20 -p 21:21 -p 60000-60100:60000-60100 python:slim bash

客户端代码

import os
from ftplib import FTP
class FTPClient:
    def __init__(self, host):
        self.host = host
    def _connect(self):
        self.ftp = FTP(self.host)
        self.ftp.set_pasv(True)
        self.ftp.login('username', 'password')
    def _disconnect(self):
        ''' Disconnect from the FTP server '''
        self.ftp.quit()
    def connect(self):
        self.ftp = FTP(self.host)
        self.ftp.login('username', 'password')
    @staticmethod
    def manageConnection(func):
        def wrapper(self, *args, **kwargs):
            self._connect()
            result = func(self, *args, **kwargs)
            self._disconnect()
            return result
        return wrapper
    @manageConnection
    def pull(self, filename: str, path: str = None, local: str = '.') -> bool:
        if path is not None:
            self.ftp.cwd(path)
        if filename in self.ftp.nlst():
            local_path = os.path.join(local, filename)
            with open(local_path, 'wb') as file:
                self.ftp.retrbinary('RETR ' + filename, file.write)
            return True
        return False
    @manageConnection
    def view(self, path: str = None) -> bool:
        if path is not None:
            self.ftp.cwd(path)
        for file in self.ftp.nlst():
            print(file)
        return False

本地访问验证(正常)

f = FTPClient('127.0.0.1')
f.connect() # works - I see logs on server
f.pull('utils.py') # works - downloads files

公网访问报错

f = FTPClient('97.117.28.178')
f.connect() # doesn't work - no log entries on server, it doesn't even reach it

Traceback (most recent call last):
File "", line 1, in
File "", line 13, in wrapper
File "", line 5, in _connect
File "/usr/local/lib/python3.11/ftplib.py", line 121, in init
self.connect(host)
File "/usr/local/lib/python3.11/ftplib.py", line 158, in connect
self.sock = socket.create_connection((self.host, self.port), self.timeout,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/socket.py", line 851, in create_connection
raise exceptions[0]
File "/usr/local/lib/python3.11/socket.py", line 836, in create_connection
sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused

## 问题原因
请求未到达服务器是核心问题,具体原因分为以下几点:
- **端口未放行**:宿主机防火墙、云服务商安全组未开放FTP所需的21(控制端口)、20(主动模式数据端口)、60000-60100(被动模式端口),公网请求被直接拦截。
- **NAT穿透问题**:如果是家庭网络,公网IP是运营商NAT后的地址,未在路由器配置端口转发,导致请求无法穿透到宿主机。
- **端口映射失效**:Docker端口映射命令未生效,宿主机未监听对应端口,请求无法转发到容器内的FTP服务。

## 解决方法
### 1. 放行所有必要端口
- **云服务器**:登录云控制台,在安全组添加入站规则,开放`21/TCP`、`20/TCP`、`60000-60100/TCP`,允许所有IP(0.0.0.0/0)访问。
- **本地宿主机**:
  - Ubuntu/Debian(ufw):
    ```bash
    sudo ufw allow 20/tcp
    sudo ufw allow 21/tcp
    sudo ufw allow 60000:60100/tcp
    sudo ufw reload
    ```
  - CentOS/RHEL(firewalld):
    ```bash
    sudo firewall-cmd --add-port=20/tcp --permanent
    sudo firewall-cmd --add-port=21/tcp --permanent
    sudo firewall-cmd --add-port=60000-60100/tcp --permanent
    sudo firewall-cmd --reload
    ```

### 2. 验证Docker端口映射
在宿主机执行命令,检查21端口是否被Docker监听:
```bash
netstat -tulpn | grep :21

若输出包含docker-proxy则映射正常,否则重新启动容器确保端口映射命令正确。

3. 家庭网络配置端口转发

在路由器后台配置端口转发规则,将路由器的21、20、60000-60100端口转发到宿主机的对应端口;若运营商封禁21端口,可更换非标准端口(如2121)。

4. 确认被动模式配置

确保masquerade_address设置为宿主机的公网IP,且被动端口范围60000-60100已在所有环节放行,避免后续数据传输失败。

内容的提问来源于stack exchange,提问作者MetaStack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 00:05:09