ftplib and pyftpdlib communication

I have a server (fedora 29) at home running (as root) an FTP made in python With ports 5000-5003 and 40000-50000 open, the machine is in DMZ

import logging
import sys

from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.authorizers import UnixAuthorizer
from pyftpdlib.filesystems import UnixFilesystem

def main():
     logger = logging.getLogger()
     ch = logging.StreamHandler(sys.stdout)
     logger.setLevel(logging.DEBUG)
     ch.setLevel(logging.DEBUG)
     formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
     ch.setFormatter(formatter)
     logger.addHandler(ch)

     authorizer = UnixAuthorizer(allowed_users=['marcelo'], require_valid_shell=True, global_perm='elradfmwMT')
     handler = FTPHandler
     handler.authorizer = authorizer
     handler.abstracted_fs = UnixFilesystem
     handler.passive_ports = (40000, 50000)

     handler.log_prefix = '%(username)s@%(remote_ip)s'

     server = FTPServer(('',5001), handler)
     server.serve_forever()

     if __name__ == '__main__':
         main()

At work I am like a machine (fedora 29) with ports 5000-5003/tcp and 40000-50000/tcp open and in DMZ running an ftplib client.

from ftplib import FTP

ftp = FTP('')
ftp.connect('*****',5001)
ftp.login(user='****',passwd='*****')
ftp.set_debuglevel(2)
ftp.set_pasv(False)
#ftp.cwd('/home/marcelo')
response = ftp.retrlines('LIST')
print("response: %s",response)


def uploadFile():
  filename = 'arquivo5.txt'
  ftp.storbinary('STOR '+filename, open(filename,'rb'))



def downloadFile():
  filename = 'backup.dmp'
  localfile = open(filename, 'wb')
  ftp.retrbinary('RETR '+filename, localfile.write, 2048)
  localfile.close()

#uploadFile()
#downloadFile()

ftp.quit()

In the client I get the following answer: ftplib.error_perm: 501 Rejected data connection to foreign address 192.168.0.9:47123.

And no server: pyftpdlib-WARNING-marcelo@... -> 501 Rejected data connection to foreign address 192.168.0.9: 47123.

Note: asterisks are my public ip Obs2: in the browser works perfectly

I believe the problem is the Type C address I am sending to the server but I don't know how to solve this.

Thank you.

Author: Marcelo Rocha, 2019-07-16

1 answers

Look - I can understand where the problem is, but I don't know how to pass you the solution.

In the FTP protocol, and other protocols over TCP (such as HTTP itself), when the first connection is made to the server-the one listening on Port 5001, the server transfers that connection to another random high Port. So the server can listen to Port 5001 again waiting for new connections. (in the case of your messages port 47123 is the high port - it can vary between different attempts, since it is basically drawn).

And what's going on there, based on your error messages is that by switching the connection to that FTP "data" connection, the server is passing its local IP-192.168.0.5 , and not the public IP (which you masked). Unfortunately I do not know enough TCP to know the correct way that the FTP server should use to change the connection to another port. At first glance I would say that the FTP server is doing right and this is a problem in their network rules: they do not know that when receiving the packet that contains the FTP data to change the connection to another port you have to "translate" the IP from the internal IP to the public IP.

In short, to make it work as it is you will have to understand how this change of connection to the high port, at the low level of the FTP protocol, and which component should be responsible for translating the internal IP to the public IP there.

Solution 1: discard the server code and use SFTP

The recommendation however is not to leave like that for some other reasons: by creating an FTP server you are reinventing the wheel, and it is not an easy wheel in particular. The FTP protocol is one of the oldest on the internet, and over 3 or decades was one of the most vunlnerable, with good network engineers taking 15, 20 years to finally have FTP servers without security flaw in their code.

A library Python that you are using certainly already incorporates many of the fixes found in that time, but possibly there are other problems at the highest level of the protocol that would be the responsibility of the server implementer to fix. As examples of the two situations - (1) the library has this object "Authorizer" there that already takes the password inside the FTP package and uses the direct authentication mechanisms of Posix, instead of something "handmade" with a local password. And (2) you turn on the server in the filesystem without checking anything, and without giving a "starting point" directory: probably (I did not read the pyftplib doc) exposes your filesystem all , including the folders /var and /etc to FTP, and all user files (including program codes, configuration files with secret production keys, everything ).

Another thing: even if you make a really secure FTP server, and with all the correct network settings: the FTP procolo is insecure by nature , why login credentials traffic mna internet without any encryption. This implies that an attacker who has access to any router, or another point in the route of your data can capture the password.

So-since you are not doing anything very special on the FTP server, it is better to leave an already consolidated server to serve - and, even more so, use the SFTP protocol - which routes the FTP data through an SSH tunnel and the problems of encryption.

Climbing up an SFTP server is a matter of two configuration lines in the OpenSSH configuration - if you want to limit sftp access only to user "marcelo", and keep SSH for other users, you'll need to configure more things - otherwise that's it. The client code (if using the FTP protocol correctly) remains exactly the same,but now with access through the SSH tunnel.

In the case of fedora even sftp is already enabled by default in the file /etc/ssh/sshd_config - it's just a matter of reading the documentation to hit the ports and user restriction (and preferably which part of the filesystem to grant access to).

Another solution, using your server code:

An alternative to SFTP if you really want to customize access to a server of your own is to use SSH then to create an SSH tunnel to connect the client to the server - this will also make the server work well without worrying about IP, and will solve the problem of data trafegando open on the internet. (even if the client is a Windows machine, the good old "PuTTY" manages to open the necessary SSH tunnel)

Whatever your option, I think you'll need it here:

Https://www.openssh.com/manual.html

 1
Author: jsbueno, 2019-07-16 20:45:46