пятница, 27 ноября 2015 г.

Установка и настройка PureFTPd с ограничением доступа пользователей по ip диапазонам/подсетям

Наш инструментарий

  1. Ubuntu Server
  2. Mysql Server
  3. Pureftpd-mysql

 

Первым делом установим весь необходимый софт

sudo apt-get update
sudo apt-get install mysql-server mysql-client pure-ftpd-mysql


Подготовим MySQL для работы

Создадим базу данных

CREATE DATABASE `PureFTPd` /*!40100 COLLATE 'utf8_general_ci' */;

После чего создадим пользователя с правами на эту базу, уровень привилегий определяйте сами. Для работы сервера будет достаточно и readonly прав.
Я назвал своего пользователя pureftptd.

Таблица для аутентификации пользователей:

CREATE TABLE `ftp` (
    `User` VARCHAR(16) NOT NULL DEFAULT '',
    `status` ENUM('0','1') NOT NULL DEFAULT '1',
    `Password` VARCHAR(64) NOT NULL DEFAULT '',
    `Uid` VARCHAR(11) NOT NULL DEFAULT '2001',
    `Gid` VARCHAR(11) NOT NULL DEFAULT '2001',
    `Dir` VARCHAR(128) NOT NULL DEFAULT '',
    `ULBandwidth` SMALLINT(5) NOT NULL DEFAULT '0',
    `DLBandwidth` SMALLINT(5) NOT NULL DEFAULT '0',
    `comment` TINYTEXT NOT NULL,
    `ipaccess` ENUM('*','special') NOT NULL DEFAULT '*',
    `QuotaSize` SMALLINT(5) NOT NULL DEFAULT '0',
    `QuotaFiles` INT(11) NOT NULL DEFAULT '0',
    PRIMARY KEY (`User`),
    UNIQUE INDEX `User` (`User`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

 
Таблица, в которой будут добавляться записи ip-range для пользователей

CREATE TABLE `ftp_ipranges` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `user` VARCHAR(16) NOT NULL,
    `ip_start` VARCHAR(15) NOT NULL,
    `ip_end` VARCHAR(15) NOT NULL,
    `comment` VARCHAR(255) NULL DEFAULT '-',
    PRIMARY KEY (`id`),
    UNIQUE INDEX `UniqFields` (`user`, `ip_start`, `ip_end`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;


Создадим системного пользователя для работы с FTP


groupadd -g 2001 ftpgroup
useradd -u 2001 -s /bin/false -d /bin/null -c "pureftpd user" -g ftpgroup ftpuser

Приступим к конфигурированию базовых параметров FTP-сервера


#Указываем в каком формате и где будет храниться дополнительный логфайл
#clf - формат Apache
#stats - формат для статических отчетов
#w3c - формат стандарта w3c
echo "clf:/var/log/pure-ftpd/transfer.log" > /etc/pure-ftpd/conf/AltLog

#Автоматически создаем директории пользователя, если она отсутствует
echo "yes" > /etc/pure-ftpd/conf/CreateHomeDir

#Закрываем пользователя в его домашней папке
echo "yes" > /etc/pure-ftpd/conf/ChrootEveryone

#Запрещаем резолвить имена хостов
echo "yes" > /etc/pure-ftpd/conf/DontResolve

#Кодировка файловой системы
echo "UTF-8" > /etc/pure-ftpd/conf/FSCharset

#Слушаем только IPv4 адреса
echo "yes" > /etc/pure-ftpd/conf/IPV4Only

#Минимальный UID, с которым сервер пустит юзера
echo "1000" > /etc/pure-ftpd/conf/MinUID

#Указываем расположение конфига для работы с MySQL
echo "/etc/pure-ftpd/db/mysql.conf" > /etc/pure-ftpd/conf/MySQLConfigFile

#Включаем PAM аутентификацию (если нужна)
echo "yes" > /etc/pure-ftpd/conf/PAMAuthentication

#Указываем диапазон портов для пассивного соединения
echo "49152 65534" > /etc/pure-ftpd/conf/PassivePortRange

#Дефолтная настройка, указывает путь к локальной базе данных юзеров
echo "/etc/pure-ftpd/pureftpd.pdb" > /etc/pure-ftpd/conf/PureDB

#Отключаем системную Unix аутентификацию
echo "no" /etc/pure-ftpd/conf/UnixAuthentication

Настройка PureFTPd для работы с СУБД MYSQL 

Делаем резервную копию конфига
cat /etc/pure-ftpd/db/mysql.conf > /etc/pure-ftpd/db/mysql.conf.bak

Зануляем конфиг
cat /dev/null > /etc/pure-ftpd/db/mysql.conf

Открываем для редактирования файл конфигурации ( nano /etc/pure-ftpd/db/mysql.conf ) и вставляем туда конфиг следующего содержания (директивы доступа к серверу MySQL, естественно используйте свои)

MYSQLServer     localhost

MYSQLPort       3306

MYSQLSocket      /var/run/mysqld/mysqld.sock
 
MYSQLUser       pureftptd
 
MYSQLPassword   pureftptdpass
 
MYSQLDatabase   PureFTPd
 
MYSQLCrypt      md5
 
MYSQLGetPW      SELECT ftp.Password FROM ftp LEFT JOIN ftp_ipranges ON ftp_ipranges.user = ftp.User WHERE ftp.User="\L"  AND status="1" AND (ipaccess = "*" OR ( INET_ATON("\R") >= INET_ATON(ftp_ipranges.ip_start) AND INET_ATON("\R") <= INET_ATON(ftp_ipranges.ip_end) ) ) LIMIT 1
 
MYSQLGetUID     SELECT ftp.Uid FROM ftp LEFT JOIN ftp_ipranges ON ftp_ipranges.user = ftp.User WHERE ftp.User="\L"  AND status="1" AND (ipaccess = "*" OR ( INET_ATON("\R") >= INET_ATON(ftp_ipranges.ip_start) AND INET_ATON("\R") <= INET_ATON(ftp_ipranges.ip_end) ) ) LIMIT 1
 
MYSQLDefaultUID 2001
 
MYSQLGetGID     SELECT ftp.Gid FROM ftp LEFT JOIN ftp_ipranges ON ftp_ipranges.user = ftp.User WHERE ftp.User="\L"  AND status="1" AND (ipaccess = "*" OR ( INET_ATON("\R") >= INET_ATON(ftp_ipranges.ip_start) AND INET_ATON("\R") <= INET_ATON(ftp_ipranges.ip_end) ) ) LIMIT 1
 
MYSQLDefaultGID 2001
 
MYSQLGetDir     SELECT ftp.Dir FROM ftp LEFT JOIN ftp_ipranges ON ftp_ipranges.user = ftp.User WHERE ftp.User="\L"  AND status="1" AND (ipaccess = "*" OR ( INET_ATON("\R") >= INET_ATON(ftp_ipranges.ip_start) AND INET_ATON("\R") <= INET_ATON(ftp_ipranges.ip_end) ) ) LIMIT 1
 
MySQLGetQTAFS   SELECT ftp.QuotaFiles FROM ftp LEFT JOIN ftp_ipranges ON ftp_ipranges.user = ftp.User WHERE ftp.User="\L"  AND status="1" AND (ipaccess = "*" OR  ( INET_ATON("\R") >= INET_ATON(ftp_ipranges.ip_start) AND INET_ATON("\R") <= INET_ATON(ftp_ipranges.ip_end) ) ) LIMIT 1
 
MySQLGetQTASZ  SELECT ftp.QuotaSize FROM ftp LEFT JOIN ftp_ipranges ON ftp_ipranges.user = ftp.User WHERE ftp.User="\L"  AND status="1" AND (ipaccess = "*" OR    ( INET_ATON("\R") >= INET_ATON(ftp_ipranges.ip_start) AND INET_ATON("\R") <= INET_ATON(ftp_ipranges.ip_end) ) ) LIMIT 1
 
MySQLGetBandwidthUL SELECT ftp.ULBandwidth FROM ftp LEFT JOIN ftp_ipranges ON ftp_ipranges.user = ftp.User WHERE ftp.User="\L"  AND status="1" AND (ipaccess = "*" OR     ( INET_ATON("\R") >= INET_ATON(ftp_ipranges.ip_start) AND INET_ATON("\R") <= INET_ATON(ftp_ipranges.ip_end) ) ) LIMIT 1
 
MySQLGetBandwidthDL SELECT ftp.DLBandwidth FROM ftp LEFT JOIN ftp_ipranges ON ftp_ipranges.user = ftp.User WHERE ftp.User="\L"  AND status="1" AND (ipaccess = "*" OR     ( INET_ATON("\R") >= INET_ATON(ftp_ipranges.ip_start) AND INET_ATON("\R") <= INET_ATON(ftp_ipranges.ip_end) ) ) LIMIT 1

После настройки наконец-то можно запустить FTP-сервер командой
sudo service pure-ftpd-mysql restart

Создание пользователей

Создание обычного пользователя:
INSERT INTO `ftp` (`User`, `status`, `Password`, `Uid`, `Gid`, `Dir`, `ULBandwidth`, `DLBandwidth`, `comment`, `ipaccess`, `QuotaSize`, `QuotaFiles`) VALUES ('testUser', '1', MD5('secret'), '2001', '2001', '/ftp/users/testUser', 0, 0, 'user for test', '*', 0, 0);

Создание пользователя, которому разрешен доступ только из сети 10.0.0.0/8
INSERT INTO `ftp` (`User`, `status`, `Password`, `Uid`, `Gid`, `Dir`, `ULBandwidth`, `DLBandwidth`, `comment`, `ipaccess`, `QuotaSize`, `QuotaFiles`) VALUES ('testUser_Special', '1', MD5('secret'), '2001', '2001', '/ftp/users/testSpecialUser', 0, 0, 'special user for test', 'special', 0, 0);
INSERT INTO `ftp_ipranges` (`user`, `ip_start`, `ip_end`, `comment`) VALUES ('testUser_Special', '10.0.0.0', '10.255.255.255', 'Вся локальная сеть'); 

И так, мы видим, что создание  пользователя, на которого накладывается ограничение доступа по ip заключается в изменении флага ipaccess в главной таблице, и созданием нового ip диапазона в таблице ftp_ipranges.

Делаем доступ для Anonymouse

Сконфигурируем сервер для работы с анонимными пользователями:

#Разрешаем анонимных пользователей
echo "no" > /etc/pure-ftpd/conf/NoAnonymous
 
#Запрещаем анонимным пользователям создавать новые директории
echo "no" > /etc/pure-ftpd/conf/AnonymousCanCreateDirs

#Запрещаем анонимным пользователям создавать заливать файлы
echo "no" > /etc/pure-ftpd/conf/AnonymousCantUpload

Делаем новую группу и пользователя
groupadd ftp
useradd -s /bin/false -d /ftp/public -m -c "anonymous ftp" -g ftp ftp

Перезапускаем демон pureftpd
sudo service pure-ftpd-mysql restart

В моем случае анонимусу разрешено только просматривать файлы, по этому тут будет достаточно сменить права доступа к директории анонима
cd /ftp
chmod 555 public/


На сим всё, мы сделали полноценный ftp-сервер, который может весьма гибко управлять доступом, ограничением скорости, дисковой квотой и количеством файлов для каждого пользователя. Дальше можно кастомизировать в любую сторону, тут ограничивает только фантазия.
Вопросы можно задать в комментариях.
При использовании материалов статьи ссылка на источник обязательна!

суббота, 7 ноября 2015 г.

Запись access_log nginx в mysql через syslog-ng

На днях озадачился вопросом записи access-лога nginx не в файл, а в СУБД. Прогуглив интернеты нашел один из вариантов решения с использованием syslog-ng, но как его реализовать готовой информации нигде нет.
Ну что ж, вызов принят, погнали!

Что мне понадобилось для реализации: Ubuntu 14.04.03, Nginx, syslog-ng,  MySQL.
Первым делом создаем базу данных, юзера с правами на эту базу, как это делать описывать не буду, статей на эту тему куча.

После делаем табличку в созданной базе.

Create-код для таблички:

CREATE TABLE `log` (
    `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
    `ip` VARCHAR(40) NOT NULL DEFAULT '0.0.0.0',
    `user` VARCHAR(15) NULL DEFAULT NULL,
    `logdate` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
    `method` VARCHAR(8) NULL DEFAULT NULL,
    `file` VARCHAR(200) NOT NULL,
    `proto` VARCHAR(10) NULL DEFAULT NULL,
    `answer` SMALLINT(3) UNSIGNED NULL DEFAULT NULL,
    `bytes` INT(10) UNSIGNED NULL DEFAULT '0',
    `referer` TEXT NULL,
    `user_agent` TINYTEXT NULL,
    `host` VARCHAR(50) NULL DEFAULT '-',
    PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;


Индексы и значения по умолчанию думаю добавите сами. 

Далее приступаем к установке и настройке syslog-ng, ставим дополнительно библиотеку, которую использует syslog-ng для работы с MySQL.

sudo apt-get update
sudo apt-get install syslog-ng libdbd-mysql


После установки приступаем к настройке syslog-ng. Открываем для редактирования файл с конфигом syslog-ng nano /etc/syslog-ng/syslog-ng.conf
 
 У меня получился вот такой конфиг:

#####
#Описываем источник логов
#### 

source nginx_local { udp(ip(127.0.0.1) port(514) tags("nginx"));};

###
#Описываем пункт назначения логов. Для нас это сервер mysql.
#Данные, выделенные жирным шрифтом 
#нужно заполнить самостоятельно
#В моем случае MyTable - это вышесозданная табличка c именем log
###

destination mysql {
sql(type(mysql)
host("localhost") username("MyUser") password("SECRET")
database("My_DataBase")
table("MyTable")
columns("ip", "user", "logdate", "method", "file", "proto" , "answer", "bytes", "referer", "user_agent", "host" )
values("$CLIENT_IP", "$USER", $TIME , "$METHOD", "$FILENAME","$PROTO", $STATUS, $BYTES,  "$REFERER", "$USER_AGENT", "$SERVER_NAME")
);
};


###
#Описываем правла парсинга логов, прилетевших от nginx
###

parser p_nginx {
    csv-parser(columns("CLIENT_IP", "USER", "TIME",
        "METHOD", "FILENAME", "PROTO",
        "STATUS", "BYTES", "REFERER",
        "USER_AGENT", "SERVER_NAME")
         flags(escape-double-char,strip-whitespace)
         delimiters(" ")
         quote-pairs('""[]')
         );
};



###
#Описываем самое главное правило
###

log{
source(nginx_local);
parser(p_nginx);
destination(mysql);
};


Сохраняем файл, и перезапускаем службу командной sudo service syslog-ng restart

И так, осталось настроить NGINX.  
  
Версия NGINX  должна быть 1.7.1 и выше!
 
Открываем главный файл конфигурации NGINX /etc/nginx/nginx.conf и в контекст http добавляем новый logformat с именем formysql.

log_format formysql '$remote_addr  $remote_user $msec $request_method "$request_filename" "$server_protocol" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$server_name"'

Далее настраиваем конфиг хоста, access_log которого хотим писать в СУБД, и в контексте server  добавляем строчку:

access_log syslog:server=127.0.0.1,nohostname formysql;

127.0.0.1 в данном случае - это адрес сервера syslog-ng.

Сохраняем файл, и перазапускаем службу NGINX командой service nginx restart


Вот такое весьма изящное и масштабируемое решение у меня получилось для решения столь нетривиальной задачи.
При использовании материалов статьи ссылка на источник обязательна!