pyftpdlib介绍

-- TOC --

搭建FTP Server有很多选项,而pyftpdlib,则是一个在Linux和Windows下都存在选项。使用它,就像使用Python标准库中的http.server一样方便。

一开始尝试了FileZilla Server,这个软件也很有名,我也一直用FileZilla Client。但是遇到几个问题不好解决:(1)账户密码莫名其妙地就不对了,要删除账号重新设置,而且密码貌似还不能有字母;(2)FTPS启动不了,这是跟Win系统匹配的问题,网上有人说是因为少了一个补丁,我看这个补丁居然是2011年出的,还是算了吧,服务器上还要别的server在跑呢。。。思来想去,就来试试pyftpdlib吧,还好我在这个Win系统里,已经预装了Python3.8。

安装搭建ftp server

安装pyftpdlib:

pip install pyftpdlib

脚本框架:

import os

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer

def main():
    # Instantiate a dummy authorizer for managing 'virtual' users
    authorizer = DummyAuthorizer()

    # Define a new user having full r/w permissions and a read-only
    # anonymous user
    authorizer.add_user('user', '12345', '.', perm='elradfmwMT')
    authorizer.add_anonymous(os.getcwd())

    # Instantiate FTP handler class
    handler = FTPHandler
    handler.authorizer = authorizer

    # Define a customized banner (string returned when client connects)
    handler.banner = "pyftpdlib based ftpd ready."

    # Specify a masquerade address and the range of ports to use for
    # passive connections.  Decomment in case you're behind a NAT.
    handler.masquerade_address = '151.25.42.11'
    handler.passive_ports = range(60000, 65535)

    # Instantiate FTP server class and listen on 0.0.0.0:2121
    address = ('0.0.0.0', 2121)
    server = FTPServer(address, handler)

    # set a limit for connections
    server.max_cons = 256
    server.max_cons_per_ip = 5

    # start ftp server
    server.serve_forever()

if __name__ == '__main__':
    main()

这段代码就是pyftpdlib官方文档中的代码,而我自己的代码,填上了0.0.0.0这个地址(我测试发现如果不写0.0.0.0,客户端连接不上),打开了masquerade_address(服务器在NAT或防火墙内,这个地址就要填服务器的公网ip)和passive_ports,修改路劲,修改用户名和密码等等,就OK了。

pyftpdlib官方文档:https://pyftpdlib.readthedocs.io/en/latest/index.html

流控

流控机制对于一个ftp server来说,是必须的,如果有人上传下载一个超大文件,把数据传输通道占满了,其它业务就连不上了。pyftpdlib有自己的流控机制。

import os

from pyftpdlib.handlers import FTPHandler, ThrottledDTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.authorizers import DummyAuthorizer


def main():
    authorizer = DummyAuthorizer()
    authorizer.add_user('user', '12345', os.getcwd(), perm='elradfmwMT')
    authorizer.add_anonymous(os.getcwd())

    dtp_handler = ThrottledDTPHandler
    dtp_handler.read_limit = 30720  # 30 Kb/sec (30 * 1024)
    dtp_handler.write_limit = 30720  # 30 Kb/sec (30 * 1024)

    ftp_handler = FTPHandler
    ftp_handler.authorizer = authorizer
    # have the ftp handler use the alternative dtp handler class
    ftp_handler.dtp_handler = dtp_handler

    server = FTPServer(('', 2121), ftp_handler)
    server.serve_forever()

if __name__ == '__main__':
    main()

通过ThrottledDTPHandler来设置read和write的速率,不过pyftpdlib不是QoS,它是一个应用层的软件,通过一个简单有效的方式打实现流控。

An important feature for an ftpd is limiting the speed for downloads and uploads affecting the data channel. ThrottledDTPHandler.banner can be used to set such limits. The basic idea behind ThrottledDTPHandler is to wrap sending and receiving in a data counter and temporary “sleep” the data channel so that you burst to no more than x Kb/sec average. When it realizes that more than x Kb in a second are being transmitted it temporary blocks the transfer for a certain number of seconds.

pyftpdlib通过临时的sleep的方式,来达到流控的效果,所以我自己的测试,就是感觉传输一卡一卡的,不过不会断,只是慢慢地传。有流控,也减少对其它业务的影响,让多业务并行的效果更好。

用户权限控制

pyftpdlib.authorizers里面有3个authorizer,我一般就用DummyAuthorizer,因为创建的用户与底层系统无关。如果想有关也是可以的,用UnixAuthorizer,或者WindowsAuthorizer。

pyftpdlib在创建用户的时候,用大小写字母来表示具体的权限:

Read permissions:

    "e" = change directory (CWD, CDUP commands)
    "l" = list files (LIST, NLST, STAT, MLSD, MLST, SIZE commands)
    "r" = retrieve file from the server (RETR command)

Write permissions:

    "a" = append data to an existing file (APPE command)
    "d" = delete file or directory (DELE, RMD commands)
    "f" = rename file or directory (RNFR, RNTO commands)
    "m" = create directory (MKD command)
    "w" = store a file to the server (STOR, STOU commands)
    "M" = change file mode / permission (SITE CHMOD command) New in 0.7.0
    "T" = change file modification time (SITE MFMT command) New in 1.5.3

所以在使用pyftpdlib时,常常看到这样的代码:

>>> from pyftpdlib.authorizers import DummyAuthorizer
>>> authorizer = DummyAuthorizer()
>>> authorizer.add_user('user', 'password', '/home/user', perm='elradfmwMT')
>>> authorizer.add_anonymous('/home/nobody')

authorizer.add_user函数中的perm就是权限控制,而add_anonymous函数,默认使用了readonly权限。

add_user函数给用户设置了一个home directory路径,如果在这个路径下,需要对不同文件夹给出不同的权限,这个时候就要用override_perm函数,做更加精细的控制。

设置FTPS服务器

总的来说,用pyftpdlib启动ftps服务比较简单,有问题好解决,而FileZilla Server的问题很难解决。

首先需要在你的环境中安装pyopenssl,pyftpdlib要调用这个模块:

pip install PyOpenSSL

然后轻微修改你的脚本:

# from pyftpdlib.handlers import FTPHandler
from pyftpdlib.handlers import TLS_FTPHandler

导入TLS_FTPHandler,

# handler = FTPHandler  # ftp传输
handler = TLS_FTPHandler  # ftps传输
# 导入private key + certification的pem文件(上半截是私钥,下半截是证书)
handler.certfile = 'my_key_cert.pem'
handler.tls_data_required = True  # 数据连接启用加密

重启,这时你的pyftpdlib启动的ftp server,就运行在TLS加密的保护之下了。

代码中的那个.pem文件如何制作,请参考这里

本文链接:https://cs.pynote.net/sf/python/202206141/

-- EOF --

-- MORE --