Задача: всё та же, настроить индивидуальный FTP-доступ к директориям веб-сайтов (/var/www/<имя хоста>) на основе виртуальных пользователей в ОС CentOS 6.5.

Однако решение проблемы будет совершенно иным. Вместо того, чтобы использовать громоздкий бюрократический аппарат аутентификации и авторизации Linux, я предлагаю за час сделать свой собственный FTP-сервер, который бы работал в точности так, как хочется мне (и вам).

Началось всё с того, что мной была обнаружена очень хорошенькая библиотека pyftpd для Python, полностью инкапсулирующая весь FTP-протокол внутри себя и предоставляющая очень богатый интерфейс для интеграции. Так вот, без лишних слов, весь FTP-сервер с виртуальными пользователями на одной странице:


from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
from ConfigParser import SafeConfigParser
from hashlib import sha1

class ConfigAuthorizer(DummyAuthorizer):
    def __init__(self, userlist_path):
        super(ConfigAuthorizer, self).__init__()

        userlist_config = SafeConfigParser()
        userlist_config.readfp(open(userlist_path))

        for section in userlist_config.sections():
            if section.lower() == 'anonymous':
                self.add_anonymous(userlist_config.get(section, 'home'))
            else:
                msg_login = None
                msg_quit = None
                username = userlist_config.get(section, 'username')

                if userlist_config.has_option(section, 'msg-login'):
                    msg_login = userlist_config.get(section, 'msg-login')
                if userlist_config.has_option(section, 'msg-quit'):
                    msg_quit = userlist_config.get(section, 'msg-quit')

                self.add_user(
                    username,
                    userlist_config.get(section, 'password'),
                    userlist_config.get(section, 'home'),
                    userlist_config.get(section, 'permissions'),
                    msg_login,
                    msg_quit)

                if userlist_config.has_option(section, 'salt'):
                    self.user_table[username]['salt'] = userlist_config.get(section, 'salt')

class ConfigSha1Authorizer(ConfigAuthorizer):
    def __init__(self, userlist_path):
        super(ConfigSha1Authorizer, self).__init__(userlist_path)

    def validate_authentication(self, username, password, handler):
        hash = sha1(password).hexdigest()

        if 'salt' in self.user_table[username]:
            hash = sha1(hash + self.user_table[username]['salt']).hexdigest()

        return self.user_table[username]['pwd'] == hash

#
# Точка входа в приложение.
#
if __name__ == '__main__':
    config = SafeConfigParser()
    config.readfp(open('kftpd.ini'))
    userlist_path = config.get('general', 'user-file')
    authorizer = ConfigSha1Authorizer(userlist_path)
    handler = FTPHandler
    handler.authorizer = authorizer
    handler.timeout = config.getint('handler', 'timeout')
    handler.banner = config.get('handler', 'banner')
    handler.max_login_attempts = config.getint('handler', 'max-login-attempts')
    ip = config.get('general', 'ip')

    if ip == 'any':
        ip = ''

    address = (ip, config.getint('general', 'port'))
    server = FTPServer(address, handler)
    server.serve_forever()

К нему прилагаются два INI файла настроек: kftpd.ini с общими параметрами приложения и users.ini, в котором, собственно, хранятся виртуальные пользователи с SHA-1 хешами их паролей. Вот типичное содержимое этих файлов:


[general]
ip = any
port = 21
user-file = users.ini

[handler]
timeout = 300
banner = kftpd is ready.
max-login-attempts = 3

# Permissions note:
# 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 mode/permission (SITE CHMOD command)
#

[User1]
username = user1
password = 2da276d67be3642b09f721e429a36126535636f0
salt = 1234
home = C:\python27_x64
permissions = elradfmwM
msg-login = Login successful!
msg-quit = Goodbye!

Осталось только правильно демонизировать эту программу в среде Linux и собственный FTP-сервер готов.

comments powered by HyperComments