I am trying to create a simple FTPS server on my Ubuntu Amazon EC2 instance using the Python library pyftpdlib.
Here is the code straight from the documentation:
#!/usr/bin/env python
"""
An RFC-4217 asynchronous FTPS server supporting both SSL and TLS.
Requires PyOpenSSL module (http://pypi.python.org/pypi/pyOpenSSL).
"""
from pyftpdlib.servers import FTPServer
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.contrib.handlers import TLS_FTPHandler
import os
def main():
authorizer = DummyAuthorizer()
authorizer.add_user('ubuntu', '*****', os.getcwd(), perm='elradfmw')
authorizer.add_anonymous('.')
handler = TLS_FTPHandler
handler.certfile = 'keycert.pem'
handler.authorizer = authorizer
handler.masquerade_address = '52.23.244.142'
# requires SSL for both control and data channel
handler.tls_control_required = True
handler.tls_data_required = True
handler.passive_ports = range(60000, 60099)
server = FTPServer(('', 21), handler)
server.serve_forever()
if __name__ == '__main__':
main()
When I run the script on my Amazon EC2 instance and when I try to connect remotely using FileZilla, I get:
Status: Connecting to 52.23.244.142:21...
Status: Connection established, waiting for welcome message...
Response: 220 pyftpdlib 1.4.0 ready.
Command: AUTH TLS
Response: 234 AUTH TLS successful.
Status: Initializing TLS...
Status: Verifying certificate...
Command: USER ubuntu
Status: TLS/SSL connection established.
Response: 331 Username ok, send password.
Command: PASS *****
Response: 230 Login successful.
Command: OPTS UTF8 ON
Response: 501 Invalid argument.
Command: PBSZ 0
Response: 200 PBSZ=0 successful.
Command: PROT P
Response: 200 Protection set to Private
Command: OPTS MLST type;perm;size;modify;unix.mode;unix.uid;unix.gid;
Response: 200 MLST OPTS type;perm;size;modify;unix.mode;unix.uid;unix.gid;
Status: Connected
Status: Retrieving directory listing...
Command: PWD
Response: 257 "/" is the current directory.
Command: TYPE I
Response: 200 Type set to: Binary.
Command: PASV
Response: 227 Entering passive mode (52,23,244,142,174,172).
Command: MLSD
Response: 150 File status okay. About to open data connection.
Error: Connection timed out
Error: Failed to retrieve directory listing
I think I am missing something. Can I get some help?
Your server must present its external IP address in the response to the PASV
command. You instead present an internal IP address within the EC2 private network, to which the FileZilla obviously cannot connect to.
While FileZilla can workaround that:
Server sent passive reply with unroutable address. Using server address instead.
other FTP clients (like the Windows command-line ftp.exe
) cannot.
Use the handler.masquerade_address
to configure the external IP address:
handler.masquerade_address = '52.23.244.142'
FileZilla cannot connect to the port 50048 (195 << 8 + 128). You have probably not opened ports in an FTP passive mode port range in the EC2 firewall.
See Setting up FTP on Amazon Cloud Server (particularly section "Step #2: Open up the FTP ports on your EC2 instance" in the best answer).
To avoid opening whole unprivileged port range, limit the FTP server to use a smaller port range using handler.passive_ports
:
handler.passive_ports = range(60000, 60099)
For a general information see my article about the network setup in respect to FTP passive (and active) connection modes.