22 октября 2014

SFTP, FTPЕS и тонкая настройка ProFTPD

    Традиционный способ передачи файлов по протоколу FTP уже считается устаревшим и небезопасным. Есть более совершенные альтернативы, например, SFTP, SCP, FTPS и WebDAV.  Но тем не менее для анонимного доступа обычный FTP вполне сгодится. Еще одна причина, по которой отказаться от FTP полностью пока нельзя, отсутствие в windows встроенной поддержки безопасных FTP. 

В этой статье описываются принципы правильной настройки сервера ProFTPD с виртуальными пользователями, перекодировкой в cp1251 и поддержкой протоколов FTPS (FTP Secure) и SFTP (Secure File Transfer Protocol). Как вы можете видеть из названий, один - безопасность протокола, а другой - безопасный протокол. В этом их кардинальное отличие!

FTPS является обычным FTP поверх SSL/TLS, как HTTPS. Есть две его разновидности: Implicit FTPS - соединение устанавливается через дополнительный порт SSL 990 и Explicit FTPS или сокращенно FTPES, это уже более новая версиякоторый сейчас всеми и ипользуется, соединение и управление по одному стандартному порту 21.

А вот SFTP это абсолютно новый протокол, разработан с нуля, основан на SSH2 и не имеет с FTP ничего общего. У него больше возможностей, например есть докачка файлов после разрыва соединения. В рамках SSH шифруется все. Конечно скорость передачи у SFTP будет меньше чем у чистого FTP или FTPS.

Я провел сравнение на 100 Мбитной сетке: FTP ~ 10 МБ/с, SFTP ~ 3 МБ/с. У FTPS такая же скорость, как и у обычного FTP. Еще для сравнения я проверил SFTP, встроенный в openssh, там скорость оказалась порядка 7 МБ/с. Почему такая разница по сравнению с proftp сказать сложно, видимо в самой реализации sftp. Попробуйте сравнить у себя, будет интересно узнать, как у вас.

Поэтому, наверно все таки, есть смысл использовать sftp-server от OpenSSH. 

Имеем FreeBSD 10, что бы сэкономить время устанавливаем из пакета.
# pkg install proftpd

proftpd.conf
Конфигурационный файл находится в /usr/local/etc/proftpd.conf. Откройте и редактируйте его под себя по ходу чтения статьи.
 
Сервер запускается через inetd или автономно.
ServerType standalone
Рекомендуется создать специального пользователя и группу FTP для запуска службы. Пароль можно сделать пустым.
# adduser ftp
и изменить в конфиге
User ftp
Group ftp
По умолчанию сервер слушает все интерфейсы, что бы указать только заданные адреса, используются директивы
DefaultAddress 192.168.20.1 192.168.30.1
SocketBindTight on
По желанию в виртуальных хостах можно переопределить другой адрес.
  
Для того, что бы включить перекодировку с UTF-8 на CP1251 нужно обязательно задать LangPath
LangEngine   on
LangPath     /usr/share/locale
UseEncoding  UTF-8 WINDOWS-1251
Что бы задать пользователю корневую директорию, за пределы, которой он не должен выходить, используется
DefaultRoot ~ 
знак "~" сообщает, что  будет использоваться домашняя директория пользователя.

Если нужно сделать исключение из правил и дать некоторым пользователям полный доступ к chroot директории, то создаете группу, например admins и пишете
DefaultRoot ~ !admins
Для управления анонимным доступом в конфигурациооном файле есть секция
<Anonymous ~ftp>
  User ftp
  Group ftp
  UserAlias anonymous ftp
  <Limit WRITE>
    DenyAll
  </Limit>
</Anonymous>
Если у вас используются виртуальные хосты, то эту секцию лучше вынести в <Global>, что бы анонимный вход был доступен на всех ip, а не только на основном!

Виртуальные пользователи
По умолчанию для аутентификации пользователей proftp смотрит файл /etc/passwd.  Но использование виртуальных пользователей безопаснее, поскольку это не позволяет злоумышленнику перехватить системные аккаунты, которые передаются в открытом виде! Для этого используются директивы AuthUserFile и AuthGroupFile в конфигурационном файле.
AuthUserFile /usr/local/etc/proftpd/ftpd.passwd
AuthUserFile используется в дополнение к passwd и не исключает его. Но если все таки нужно его запретить, то используйте AuthOrder.
AuthOrder mod_auth_file.c
Форматы файлов AuthUserFile и AuthGroupFile такие же, как /etc/passwd и /etc/group соответственно.
username:password:uid:gid:gecos:homedir:shell
groupname:grouppasswd:gid:member1,member2,...memberN
Для создания и изменения этих файлов используется специальный скрипт ftpasswd. Первый параметр, который надо задать, это --passwd или --group. Потом --name, --uid, --home и --shell. Например,
# ftpasswd --passwd --name=bob --uid=1001 --home=/home/bob --shell=/bin/false
Uid должен быть от реального пользователя, например от ftp, таким образом из одного реального пользователя создается много виртуальных.

После выполнения ftpasswd создается файл ftpd.passwd. Для создания файла с другим именем и расположением используется опция --file.

Аналогично для групп
# ftpasswd --group --name=group-name --gid=group-id --member=user-member1 \ --member=user-member2 ... --member=user-memberN
Так же можно использовать параметр --hash для вычисления хэша либо пароля.
По умолчанию proftp не пускает пользователей у которых шел не перечислен в файле /etc/shell. Поэтому, если вы используете недействительный шел, например /usr/sbin/nologin, то обязательно задайте директику RequireValidShell off. 

Ограничение доступа
У proftpd есть удобный механизм ограничения доступа. Создаются различные правила для клиента на выполнение FTP команд. Это может быть запрет на переименование, удаление, запись файлов в определенную папку и др. (полный список можно посмотреть на сайте).

Для создания правил в конфигурационном файле указывается секция <Limits>... </Limits> с нужными командами. Их можно просто перечислить или использовать группы ALL, DIRS, LOGIN, READ и WRITE.

Затем внутри секции определяется действие: разрешить или запретить: DenyAll, AllowAll, AllowUser, AllowGroup, Deny from, Allow from. Их порядок играет имеет значение, если порядок "Allow,Deny", то разрешающее правило важнее, если наоборот "Deny,Allow", то запрет.

Например,  следующие команды разрешают смену директории и загрузку файлов в эту директорию, при условии, что файловая система так же имеет соответствующие разрешения.
<Limit ALL>
  DenyAll
</Limit>
<Limit CDUP CWD PWD XCWD XCUP>
  AllowAll
</Limit>
<Limit STOR STOU>
  AllowAll
</Limit>  
Для этой цели можно использовать группу WRITE, но тогда пользователь сможет так же создавать и удалять поддиректории.

Или еще один пример, разрешается авторизация только двум пользователям и группе.
<Limit LOGIN>
  AllowUser barb
  AllowUser dave
  AllowGroup ftpuser
  DenyAll
</Limit>
Поскольку активный режим работы FTP-сервера не безопасен, это когда сервер должен сам подключиться к клиенту. То его можно отключить средствами <Limits>.
<Limit EPRT PORT>
  DenyAll
</Limit>
Обычно <Limits> удобно использовать внутри секции <Directory>, тогда все вложенные поддиректории будут наследовать свойства. Следующий пример описывает папку, у которой нельзя увидеть содержимое, но можно загружать и скачивать файлы.
<Directory /path/to/dir>
  <Limit LIST NLST MLSD MLST STAT>
   DenyAll
  </Limit>
</Directory>
Если в пути вы указываете  суффикс /*, то это означает, что конфигурация применяется только для содержимого, но не для самой папки.

Пример, назначается папка для загрузок для анонимного доступа.
<Anonymous ~ftp>
 User ftp
 Group ftp
 UserAlias anonymous ftp
 <Limit WRITE>
  DenyAll
 </Limit>
 <Directory upload/*>
  <Limit STOR>
    AllowAll
  </Limit>
 </Directory>
</Anonymous>
В ftp протоколе команда STOR служит для аплоада файлов клиентом.

Еще можно использовать классы для группировки пользователей.
<Class internal>
  From 192.168.0.0/16
  From *.example.com
</Class>
А потом назначить действия сразу целым классам с помощью AllowClass и DenyClass.
<Limit ALL>
  AllowClass internal
  DenyAll
</Limit>
По умолчанию proftpd слушает все интерфейсы, но если надо только один, то используйте параметр DefaultAddress.

Сервер за NAT
Особый случай, если ваш ftp-сервер находится за NAT и вы хотите предоставить к нему доступ извне. Для этого вам надо задать опцию MasqueradeAddress с ip-адресом nat-сервера. А на NAT-сервере настроить форвардинг порта 21 и диапазона портов для пассивного режимакоторые задаются в конфиге PassivePorts 40000-45535. 

Но как только вы зададите опцию MasqueradeAddress, пользователи из локальной сети не смогут больше подключаться! Выходом из этой ситуации будет использование данной опции не в глобальных настройках, а на виртуальном хосте.
<VirtualHost your_local_ip>
  MasqueradeAddress nat_ip
  Port 1121
</VirtualHost>

Теперь FTP-сервер, кроме 21, еще будет работать на 1121 порту. На NAT-сервере настраиваем перенаправление портов. В моем случае это ядерный NAT.
nat 1 config log if rl0 reset same_ports deny_in redirect_port tcp 192.168.15.2:1121 21 redirect_port tcp 192.168.15.2:40000-45535 40000-45535
add 1085 nat 1 ip from any to any via rl0
Один важный момент, в списке правил перед строкой nat не должно быть явно разрешающих правил для используемых портов, т.е. таких
1030 allow tcp from any to me 21, 20, 40000-45535 
Иначе пакеты не дойдут до nat и соответственно не будут перенаправляться. Была у меня такая проблема, долго не мог понять почему не работает ftp через nat, а они просто напросто не доходили до редиректа.

Еще, если вы пытаетесь таким же способом редиректить штатный фряшный ftpd через ipfw, то ничего не получится! У него нет такой замечательной опции MasqueradeAddress. В этом случае надо использовать ftp-proxy.

Теперь внутри локальной сети FTP сервер будет работать, как обычно на 21 порту. Из вне доступ так же будет по 21 порту через nat-сервер, благодаря форвардингу  redirect_port tcp 192.168.15.2:1121 21.

Виртуальные хосты
Это возможность организовать одновременно несколько ftp-серверов на одном. Они называются виртуальными. Но имейте в виду, что протокол FTP не поддерживает виртуальные хосты на основе имен. Например, нельзя создать soft.server.ru и video.server.ru, как в Apache. FTP виртуальные хосты могут быть основаны только на уникальности пары IP/порт.

Поэтому, если ваш сервер имеет несколько ip-адресов, например, локальная сеть и внешняя, то для каждой из них нужно настроить виртуальные хосты. Это делается с помощью секции  <VirtualHost>.

<VirtualHost 10.0.0.1>
   ServerName "My local FTP server"
</VirtualHost>
<VirtualHost 1.2.3.4>
   ServerName "My
external FTP server"
</VirtualHost>
 
Так же можно использовать виртуальные хоcты с одинаковым ip-адресом, но на разных портах.
<VirtualHost myhost.mydomain.com>
  Port 2001
  ...
</VirtualHost>
  
 
Все глобально установленные параметры в конфигурационном файле не относятся к виртуальным хостам, их нужно задавать отдельно для каждого! Поэтому ради удобства, что бы не дублировать общие параметры в каждом виртуальном хосте, их можно вынести в секцию  <Global>, тогда они будут доступны для всех.

В <Global> так же следует вынести секцию <Anonimous ~>

Таким образом, с помощью виртуальных хостов, мы одновременно запустим ftp, sftp и ftps на разных портах.

Но для этого сначала нужно перекомпилировать ptoftpd с дополнительными модулями.
#./configure --with-modules=mod_sftp:mod_tls:mod_sql:mod_sql_mysql \
--with-includes=/usr/local/include/mysql \
--with-libraries=/usr/local/lib/mysql
# make
# make install
Выяснилось, что в портах Freebsd, начиная c версии 1.3.4 Proftpd стал использовать динамически подключаемые модули. Поэтому пересборка не требуется, достаточно указать директиву LoadModul в proftpd.conf.

Выносим общие параметры в секцию
<Global>
   PassivePorts 40000 45535
   AuthUserFile /usr/local/etc/proftpd/ftpd.passwd
   RequireValidShell no
   MaxLoginAttempts 6
   LangEngine on
   UseEncoding UTF-8 WINDOWS-1251
   DefaultRoot ~ !admins
</Global>
Подгружаем SFTP модуль
LoadModule mod_sftp.c
И добавляем виртуальный хост
<VirtualHost 192.168.10.1>
  MasqueradeAddress myhost.mydomain.com
  SFTPEngine on
  Port 8022
  SFTPHostKey /etc/ssh/ssh_host_dsa_key
  SFTPHostKey /etc/ssh/ssh_host_rsa_key
  SFTPHostKey /etc/ssh/ssh_host_ecdsa_key
  SFTPLog /var/log/sftp.log
  SFTPCompression off
</VirtualHost>
Подгружаем TLS модуль
LoadModule mod_tls.c
Добавляем виртуальный хост
<VirtualHost 192.168.10.1>
MasqueradeAddress
myhost.mydomain.com
TLSEngine on
Port 1021
TLSLog /var/log/tls.log
TLSProtocol SSLv23
TLSRequired auth
TLSOptions NoCertRequest
TLSRSACertificateFile /usr/local/etc/proftpd/ssl/cert.pem
TLSRSACertificateKeyFile /usr/local/etc/proftpd/ssl/key.pem
TLSVerifyClient off

</VirtualHost>
Параметр TLSRequired определяет политику безопасности, когда использовать SSL/TLS и может иметь несколько значений:
  • off - Клиенты могут подключаться, как по обычному FTP, так и используя шифрование,
  • ctrl - шифруется только управляющий канал, поэтому пароли будут спрятаны,
  • data - шифруются только данные (не рекомендуется),
  • on - шифруются оба канала,
  • auth - шифрование управляющего канала, но только в момент авторизации,
  • auth+data - шифрование авторизации и данных, клиенту разрешается использовать команду CCC (Clear Command Channel), что позволяет лучше работать через фаервол.

Для FTPS еще нужно сгенерировать сертификат OpenSSL и положить в /usr/local/etc/proftpd/ssl/
# openssl req -new -x509 -days 365 -nodes -out /usr/local/etc/proftpd/ssl/cert.pem -keyout /usr/local/etc/proftpd/ssl/key.pem
Добавим автозапуск в rc.conf 
# echo 'proftpd_enable="YES"' >> /etc/rc.conf
Не забываем про ротацию логов
# vi /etc/newsyslog.conf
/var/log/proftpd.log 600 7 * @T00 JC
Чтобы запустить Proftpd используем команду
# service proftpd start
По умолчанию сервер пытается связать ip-адрес и имя хоста. Поэтому, если они не соответствуют действительности,  вы получите следующее сообщение при старте.
warning: unable to determine IP address of 'hostname'
Тут либо правильно настройте DNS, либо добавьте имя в файл /etc/hosts.
# vi /etc/hosts
192.168.10.5 your_domain_name 
Проверим, что слушаются 21, 1021 и 8022 порты.
# sockstat
USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS
ftp proftpd 87980 0 tcp4 *:21 *:*
ftp proftpd 87980 1 tcp4 *:8022 *:*
ftp proftpd 87980 2 tcp4 *:1021 *:*
Теперь можно подключаться любым клиентом, например, Filezilla, он понимает ftps, ftpes и sftp. Так же удобно использовать ftps/ftpes через Total Commander, но к нему еще нужно скачать OpenSSL библиотеки. Их можно взять тут, просто запустите установщик и он сам скопирует нуждные dll в системную папку. После этого в свойствах ftp-соединения станет доступной галочка SSL/TLS.

Что бы  посмотреть активных пользователей есть команда ftpwho.

Mysql
Кто хочет можно хранить пользователей в базе mysql. Для этого нужно поставить порт proftpd-mod_sql_mysql. Он автоматически установит mysql-client. Но mysql-server вам надо поставить самим.Кстати хороший мануал на русском языке есть на сайте mysql.ru. После установки нужно создать конфигурациооный файл my.cnf и положить его в /var/db/mysql/my.cnf. Шаблоны лежат тут /usr/local/share/mysql/. Можно восползоваться, например my-medium.cnf. В него я добавил такие параметры.
[client]
character_set_client=utf8

[mysqld]
character-set-server=utf8
init-connect="SET NAMES utf8"
skip-networking
#log-bin=mysql-bin
#binlog_format=mixed
Еще необходимо проинициализировать таблицы привилегий. 
# cd /usr/local
# ./bin/mysql_install_db
# chown -R mysql:mysql /var/db/mysql
Скрипт mysql_install_dbсоздает две базы данных: одна mysql, она обеспечивает привилегии доступа и другая test для тестирования.  Для пользователя root устанавливается  пустой пароль.
Добавляем в rc.conf строчку
# echo 'mysql_enable="YES"' >> /etc/rc.conf
Запускаем MySQL сервер
# service mysql-server start
Убеждаемся, что он запустился
# ps ax | grep mysql
15840 - Is 0:00,17 /bin/sh /usr/local/bin/mysqld_safe --defaults-extra-file
16374 - I 0:01,01 /usr/local/libexec/mysqld --defaults-extra-file=/var/db/
Устанавливаем пароль для пользователя root.
# /usr/local/bin/mysqladmin -uroot password 'password'
Заходим
# mysql -uroot -p'pasword'
Позднее пароль можно поменять так
mysql> use mysql;
mysql> update user set password=PASSWORD('my_password') where user='root' and host='localhost';
mysql> flush privileges;
mysql> quit
Чтобы каждый раз не вводить пароль его можно сохранить в файле .my.cnf
# touch /root/.my.cnf
# chmod 0600 /root/.my.cnf
# vi /root/.my.cnf
  [client]
  password=passwd
И для входа просто писать из под руда # mysql

Теперь нужно создать таблицы users и groups, где будут храниться пользователи. Но сначала создадим базу данных proftpd и пользователся ftp для подключения к базе.
mysql> CREATE DATABASE proftpd;
mysql> grant select,insert,update,delete on proftpd.* to ftp@localhost identified by 'password';
mysql> USE proftpd;
mysql> CREATE TABLE users (
userid VARCHAR(30) NOT NULL UNIQUE,
passwd VARCHAR(80) NOT NULL,
uid INTEGER,
gid INTEGER,
homedir VARCHAR(255),
shell VARCHAR(255) );
mysql> CREATE TABLE groups (
groupname VARCHAR(30) NOT NULL,
gid INTEGER NOT NULL,
members VARCHAR(255) );
Добавим нового пользователя для доступа к ptoftpd
mysql> insert into users values('new_user',ENCRYPT('passwd'),'5001','5001','/home/new_user','/sbin/nologin');
mysql> exit;
Изменить можно так
mysql> UPDATE users SET passwd=ENCRYPT('new_pass'), gid=14 WHERE userid='user';
Удалить так
mysql> DELETE FROM users WHERE userid='user';
Создаем домашний каталог пользователя
# mkdir /home/new_user 
# chown 5001:5001 /home/new_user
Настраиваем proftpd для авторизации через mysql
AuthOrder mod_sql.c
SQLConnectInfo proftpd@localhost:3306 ftp password
SQLAuthTypes Crypt
SQLUserInfo users userid passwd uid gid homedir shell
DefaultRoot ~
RequireValidShell off
SQLGroupInfo groups groupname gid members
SQLAuthenticate users groups

SQLLogFile /var/log/proftpd.log
Опцию SQLLogFile включается только на момент отладки, потом не забудьте ее закомментировать! 

Логирование
Еще, что касается логов. По умолчанию proftpd ведет логирование используя  syslog. Размещение определяется согласно /etc/syslog.conf в /var/log/xferlog.

Можно настраивать различные уровни логирования: err, notice, warn, info и debug, но они распространяются только на syslog.
SyslogLevel notice
Если хочется иметь отдельные файлы логов, то можно прописать:
TransferLog /var/log/proftpd-transfer.log
SystemLog /var/log/proftpd-error.log
ExtendedLog /var/log/proftpd-extended.log
Детализация варьируется от 0 до 9 - самая подробная.

DebugLevel 9
Можно перезагружать proftpd и проверять.
# service proftpd restart
Удачных, Вам, настроек!

3 комментария:

  1. Статья на 5 балов
    Только вот одна мелочь, не имеет эффекта DefaultAddress
    sockstat упорно выдает
    nobody proftpd 1331 0 tcp4 *:21 *:*
    proftpd Version: 1.3.5_4

    ОтветитьУдалить
    Ответы
    1. # Привязка к интерфейсу
      #DefaultServer on
      SocketBindTight on
      DefaultAddress em0
      ########################

      Удалить
  2. Отлично! Решил проблему с подключением через TLS, спасибо!

    ОтветитьУдалить