Облака — белогривые лошадки или безопасный ownCloud для «маленьких» в FreeNAS

в 8:45, , рубрики: cloud storage, fail2ban, freebsd, freenas, mariadb, ngnix, owncloud, php, push notifications, push-уведомления, pushover, облачные сервисы, облачные хранилища, Серверное администрирование, хранение данных

image
ownCloud, как утверждает Википедия — это Свободное и открытое веб-приложение для синхронизации данных, расшаривания файлов и удалённого хранения документов в «облаке». И, как мне кажется, довольно интересное решение для организации собственного домашнего облака.

Однако, ownCloud, устанавливающийся в виде плагина в системе FreeNAS, да и просто из коробки, имеет ряд недостатков, от которых хотелось бы избавится даже при использовании дома:

  • Во-первых, устанавливается в связке с SQLite, что подходит только если у вас небольшое кол-во файлов и пользователей, и абсолютно не подходит, если вы планируете синхронизацию с помощью клиента. У меня же хранилище уже расползлось почти на 5Tb и установленный таким образом ownCloud просто отказывался видеть часть файлов. Да и без синхронизации отдача от облака не велика. Заменим базу данных на MariaDB.
  • Во-вторых, отсутствует работа по https, а мне совсем не нравится мысль о том, что кто-то может перехватить мои файлы. Включим https.
  • В-третьих, начисто отсутствует защита от банального подбора пароля методом брутфорса. Защитимся от брутфорса с помощью fail2ban.
  • В-четвёртых, мне лень часто просматривать логи на предмет взлома, но очень хочется оперативно узнавать о таких попытках. Настроим push-оповещения о попытках подбора пароля с помощью сервиса pushover.net.


Хочу сразу оговориться. Я не являюсь IT-специалистом, я всего лишь менеджер проектов в одном из российских системных интеграторов, а данная «инструкция» родилась в попытках настроить все 4 указанных пункта на своей домашней системе, FreeNAS работающей из-под Esxi. Инструкция написана новичком для новичков, поэтому если где-то есть явные ляпы или ошибки в командах и конфигах, то прошу указать их в комментариях.

Все настройки будем делать из консоли, ручками, максимально приближено к тому, как это, как мне видится, делают «взрослые» айтишники.

1 Подготовка Jail для ownCloud

1.1 Создаём Jail

Данный шаг описывать не буду. Если у вас получилось установить FreeNAS и он у вас работает, то проблем с данным шагом у вас быть не должно. Создание первого Jail может занять довольно продолжительное время.

1.2 Открываем доступ по SSH к данному Jail

Удобнее всего дальнейшую настройку выполнять не через web-терминал, а через полноценную программу-терминал. Например, putty. Для этого откроем доступ по SSH к нашему Jail и создадим нового пользователя, от которого и будем проводить дальнейшую настройку.

Выбираем в web-интерфейсе FreeNAS созданный нами Jail и нажимаем внизу кнопку Shell.

image

В веб-консоли Jail вводим:

# sysrc sshd_enable="YES"

Запускаем демон для работы ssh:

# service sshd start

Создаём пользователя от имени которого будем потом настраивать нашу систему. Пользователь будет иметь привилегии superuser, для чего мы включим его в группу wheel.

# adduser

Настройки adduser
Usrname: Новый пользователь от имени которого мы будем производить все манипуляции в терминале. Вводим, например, superstepa.
Full name: Полное имя этого пользователя. Вводим, например, Dyadya Stepa Policeman.
Uid (Leave empty for default): Ну раз просят оставить пустым, то так и сделаем. Смело жмём Enter.
Login group [superstepa]: Мы хотим чтобы наш пользователь обладал всеми правами местного администратора — superuser, а следовательно включаем его в группу wheel.
Login group is wheel. Invite superstepa into other groups? []: Нажимаем Enter.
Login class [default]: Жмём Enter.
Shell (sh csh tcsh git-shell nologin) [sh]: Оставляем по-умолчанию sh. Просто жмём Enter.
Home directory [/home/superstepa]: И опять Enter.
Home directory permissions (Leave empty for default): И снова Enter.
Use password-based athentications? [yes]: Хотим чтобы аутентификация этого пользователя проходила по паролю? Конечно да! Жмём Enter.
Use an empty password? (yes/no) [no]: Мы же за безопасность и не хотим чтобы у нового пользователя с правами superuser был пустой пароль. А значит очередной раз жмём Enter.
Use a random password? (yes/no) [no]: Я просто уверен, что придуманный нами пароль самый надежный. И мы хотим использовать именно его. А следовательно Enter.
Enter password: Ага, а вот и он. Вводим свой пароль.
Enter password again: Вводим его снова.
Lock out the account after creation? [no]: Нет, блокировать этот аккаунт не нужно. Просто Enter.
OK? (yes/no): Проверяем всё ли верно и если да, то пешем yes.
Add another user? (yes/no): Других пользователей нам не надо. No.

2 Установка и подготовка к работе ownCloud

Присоединяемся с помощью программы-терминала к нашему Jail.
Вводим имя созданного нами пользователя и его пароль.
На приглашение командной строки $ пишем su. Теперь приглашение командной строки сменится на что-то типа root@ownCloud:/usr/home/superstepa #, а все наши команды будут выполняться от имени superuser.
Для упрощения, приглашение командной строки я буду обозначать символом #, а мои комментарии, которые вводить не надо, буду начинать с //.

2.1 Устанавливаем необходимые пакеты

Сначала обновляем текущие пакеты:

# pkg upgrade

Затем устанавливаем необходимые для работы ownCloud пакеты (на все возникающие вопросы отвечаем yes):

# pkg install mariadb100-server php56-extensions php56-bz2 php56-curl php56-exif php56-fileinfo php56-gd php56-mbstring php56-mcrypt php56-pdo_mysql php56-openssl php56-zip php56-zlib pecl-APCu pecl-intl

Для того, чтобы включить работу по https, собираем с нужными нам пакетами, а затем и устанавливаем из портов, web-сервер nginx:

# portsnap fetch extract   // скачиваем копию коллекции портов, это может занять много времени
# cd /usr/ports/www/nginx && make config   // конфигурируем и собираем web-сервер nginx

В процессе сборки убеждаемся, что выбраны следующие пакеты:

IPV6
HTTP
HTTP_CACHE
HTTP_DAV
HTTP_FLV
HTTP_GZIP_STATIC
HTTP_PERL
HTTP_REWRITE
HTTP_SSL
HTTP_STATUS
WWW

# make install

2.2 Создаём самоподписанные ключи и сертификаты
# cd /usr/local/etc/nginx/

Создаём корневой ключ server.key (алгоритм шифрования des3, длиной 1024 bit):

# openssl genrsa -des3 -out server.key 1024

Для этого система дважды попросит ввести парольную фразу. Придумываем её и вводим.

Создаём корневой сертификат:

# openssl req -new -key server.key -out server.csr           

Отвечать на вопросы можно как угодно. Главное:
— на первый запрос Enter pass phrase for server.key ввести правильный пароль от корневого ключа созданного ранее,
— обязательно ответить на все вопросы, иначе клиент ownCloud в дальнейшем может отказаться от синхронизации файлов,
— запомнить введённые данные, чтобы в дальнейшем можно было легко вспомнить, что сертификат именно Ваш,
— запомнить пароль введённый на вопросе A challenge password []:.
Можно изменить срок действия нашего сертификата, например до 10000 дней, добавив в команду выше аргумент -days 10000.

# cp server.key server.key.org   //копируем наш ключ
# openssl rsa -in server.key.org -out server.key   //удаляем пароль из ключа
# openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt // Генерируем сертификат

2.3 Включаем автозапуск web-сервера, PHP и базы данных

# sysrc nginx_enable="YES" php_fpm_enable="YES" mysql_enable="YES"

2.4 Устанавливаем редактор для удобного редактирования конфигов

Всё-таки мы еще «маленькие» и пользоваться предустановленным редактором vi нам еще сложновато, поэтому поставим простенький редактор nano (на все возникающие вопросы отвечаем yes).

# pkg install nano

Внимание:
После установки редактора, попробуйте его запустить командой nano. В каких-то непонятных пока мне случаях что-то идёт не так и вместо запуска появляется следующая ошибка:

Shared object «libiconv.so.2» not found, required «libgmoudle-2.0.so.0

Для того чтобы это исправить выполним всего 2 команды:

# pkg delete -f gettext
# pkg upgrade

2.5 Корректируем конфиг веб-сервера nginx

Как настоящие администраторы, всегда перед корректировкой конфига делаем его копию, чтобы в случае возникновения проблем всегда можно было откатиться назад:

# cp /usr/local/etc/nginx/nginx.conf /usr/local/etc/nginx/nginx.old

И редактируем файл конфига:

# nano /usr/local/etc/nginx/nginx.conf

Всё содержимое конфига заменяем на следующее:
worker_processes 2;
 
events {
    worker_connections  1024;
}
 
http {
    include      mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    access_log  logs/access.log  main;
    sendfile        off;
    keepalive_timeout  65;
    gzip off;
     ssl_certificate /usr/local/etc/nginx/server.crt;                       // для того чтобы работал https
     ssl_certificate_key /usr/local/etc/nginx/server.key;               // для того чтобы работал https
    
          server {
             listen 443 ssl;                                                                 // для того чтобы работал https
             root /usr/local/www;
             location = /robots.txt { allow all; access_log off; log_not_found off; }
             location = /favicon.ico { access_log off; log_not_found off; }
             location ^~ /owncloud {
                 index index.php;
                 try_files $uri $uri/ /owncloud/index.php$is_args$args;
                 client_max_body_size 512M;                       // максимальный размер файла для загрузки
                 location ~ ^/owncloud/(?:.|data|config|db_structure.xml|README) {
                deny all;
                 }
            location ~ .php(?:$|/) {
                fastcgi_split_path_info ^(.+.php)(/.*)$;
                fastcgi_pass unix:/var/run/php-fpm.sock;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $fastcgi_path_info;
                include fastcgi_params;
                fastcgi_param MOD_X_ACCEL_REDIRECT_ENABLED on;
            }
            location ~* .(?:jpg|gif|ico|png|css|js|svg)$ {
                expires 30d; add_header Cache-Control public;
            }
        }
    }
}

Выход из редактора по нажатию Ctrl+X. Не забываем при выходе сохранять изменения.

2.6 Корректируем конфиг php

# cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini
# nano /usr/local/etc/php.ini

В файле находим следующие строки (используем Ctrl+W для поиска) и даём им указанные значения:
always_populate_raw_post_data = -1   // не забываем убирать ; в начале строки
date.timezone = Europe/Moscow   // либо указать Ваш часовой пояс
cgi.fix_pathinfo=0
upload_max_filesize = 512M   // либо указать максимальный размер файла доступный для загрузки в наше облако
post_max_size = 512M   // либо указать максимальный размер файла доступный для загрузки в наше облако

2.7 Корректируем php-fpm.conf:

# cp /usr/local/etc/php-fpm.conf /usr/local/etc/php-fpm.old
# nano /usr/local/etc/php-fpm.conf

В файле находим следующие строки (используем Ctrl+W для поиска) и даём им указанные значения:
listen = /var/run/php-fpm.sock
listen.owner = www   // не забываем убирать ; в начале строки
listen.group = www
env[PATH] = /usr/local/bin:/usr/bin:/bin

2.8 Корректируем /var/db/mysql/my.cnf:

# cp /var/db/mysql/my.cnf /var/db/mysql/my.old
# nano /var/db/mysql/my.cnf

В файле будет пусто, поэтому внесём в него следующие строки:
[server]
skip-networking
skip-name-resolve
innodb_flush_method = O_DIRECT
skip-innodb_doublewrite
innodb_flush_log_at_trx_commit = 2
innodb_file_per_table
expire_logs_days = 1

2.9 Запускаем веб-сервер nginx, PHP, MariaDB и настраиваем базу данных:

# service nginx start && service php-fpm start && service mysql-server start

Если всё было сделано верно, то всё запустится без ошибок и можно попробовать пройти в браузере по адресу https://<YOUR_JAIL_IP>.
Нам напомнят, что сертификат у нас самоподписанный, но приняв его мы попадём на страницу с надписью 403 Forbidden.

image

Настраиваем базу данных MariaDB:

# mysql_secure_installation

Настройки MariaDB:
Enter current password for root (enter for none): По умолчанию пароля нет, жмём Enter.
Set root password? [Y/n]: Вводим Y.
New password: Вводим новый пароль root.
Re-enter new password: Повторяем введённый ранее пароль.
На все остальные вопросы отвечаем Y, либо просто жмём Enter

Настраиваем базу данных ownСloud введя свои значения, где owncloud — название базы, ownclouduserdb — имя пользователя для работы с базой, а passwordownclouddb — пароль этого пользователя:

# mysql -u root -p
    CREATE DATABASE owncloud;
    GRANT ALL PRIVILEGES ON owncloud.* TO 'ownclouduserdb' IDENTIFIED BY 'passwordownclouddb';
    FLUSH PRIVILEGES;
    quit;

2.10 Скачиваем и устанавливаем актуальную версию OwnCloud

По ссылке смотрим текущую актуальную версию ownCloud. На момент написания статьи это была версия 8.0.2.

Скачиваем архив, где вместо 8.0.2 указываем текущую актуальную версию:

# fetch "http://download.owncloud.org/community/owncloud-8.0.2.tar.bz2"

Разархивируем:

# tar jxf owncloud-*.tar.bz2 -C /usr/local/www

Удаляем ненужный теперь нам архив:

# rm owncloud-*.tar.bz2

Назначаем системных владельцев (пользователя и группу) ownCloud:

# chown -R www:www /usr/local/www/owncloud /mnt/files

2.11 Создаём задание в crone:

# setenv EDITOR nano   // для редактирования будем использовать установленный нами редактор nano
# crontab -u www -e

Прописываем в нём:

*/15 * * * * /usr/local/bin/php -f /usr/local/www/owncloud/cron.php

Если всё было прописано верно, то мы получим следующее системное сообщение:

crontab: installing new crontab

Идём теперь в браузере по адресу https://<YOUR_JAIL_IP>/owncloud и производим там последние настройки. Не забываем, что нам надо изменить тип применяемой базы данных, а для этого нажмём Хранилище и база данных и выберем наш тип базы: MySQL/MariaDB.
image

Заполняем поля
Имя пользователя: Имя администратора нашего облака. Например, Stepanadministratovich.
Пароль: Пароль администратора.
Каталог с данными: Я предпочитаю /mnt/files/. В этот каталог я потом монтирую существующие у меня Volumes хранилища FreeNAS. Если надо объяснить как, то пишите в комментариях.
Пользователь базы данных: Мы его создавали ранее на шаге 2.9 ownclouduserdb.
Пароль базы данных: Также назначали ранее на шаге 2.9 passwordownclouddb.
Название базы данных: Всё тот-же шаг 2.9 owncloud.

Наш ownCloud уже готов к работе.

image

3 Дополнительные настройки


3.1 Просим поисковых роботов (Yandex, Google и т.д.) не индексировать наш сайт:

# ln -s /usr/local/www/owncloud/robots.txt /usr/local/www

4 Защита от подбора пароля


4.1 Устанавливаем fail2ban из портов:

# cd /usr/ports/security/py-fail2ban
# make install clean

Структура каталогов fail2ban
Fail2ban лежит по следующему пути: /usr/local/etc/fail2ban. Структура расположенных там каталогов и файлов:
папка action.d — содержит файлы действий
папка filter.d — файлы фильтров
файл fail2ban.conf — основной файл конфигурации
файл jail.conf — файл настройки защиты конкретных сервисов

4.2 Настраиваем логирование в ownCloud:

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

touch /var/log/owncloud-acces.log

Файл должен быть доступен для записи пользователю www:

# cd /var/log/
# chown www:www owncloud-acces.log

Включаем логирование неудачных входов в ownCloud:

# nano /usr/local/www/owncloud/config/config.php

В файле находим или добавляем перед самой последней строкой следующие строки (пользуйтесь поиском Ctrl+W) и даём им указанные значения:
'logtimezone' => 'Europe/Moscow',                   // или другой Ваш часовой пояс
'logfile' => '/var/log/owncloud-acces.log',
'loglevel' => '2',
'log_authfailip' => true,                               

Проверим ведётся ли логирование неудачных входов:
Несколько раз пробуем войти в web-интерфейс ownCloud с заведомо неправильным паролем или именем пользователя.

Затем выполним в консоли команду:

# nano /var/log/owncloud-acces.log

Если всё сделано правильно, то в файле появятся записи вида:

{»reqId":«es09787k250rv52fu0iu44124z494687»,«remoteAddr»:«192.168.1.1»,«app»:«core»,«message»:«Login failed: 'Admin' (Remote IP: '192.168.1.10', X-Forwarded-For: '')»,«level»:2,«time»:«2015-04-04T18:59:50+03:00»}

4.3 Создаём файл фильтра для fail2ban:

nano /usr/local/etc/fail2ban/filter.d/owncloud.conf

В файле пишем следующее:
[Definition]
failregex={"app":"core","message":"Login failed: user '.*' , wrong password, IP:HOST>","level":2,"time":".*"}   // для версий ownCloud<= 7.0.1
          {"app":"core","message":"Login failed: '.*' (Remote IP: '<HOST>', X-Forwarded-For: '.*')","level":2,"time":".*"}   // для версий  ownCloud=7.0.2-7.0.5
          {"reqId":".*","remoteAddr":"<HOST>","app":"core","message":"Login failed: .*","level":2,"time":".*"}   // для версий  ownCloud>=8

По сути это парсер, который во всей той служебной информации, которую ownCloud пишет в свой лог, должен найти ip-адрес того, кто пытался подобрать пароль на вход. Те элементы, что никогда не меняются в записях лога — указаны тут в явном виде. Те, что меняются — заменены на *. Ну а собственно ip-адресс, который мы ищем, заменён на переменную <HOST>.

4.4 Редактируем файл настроек сервисов:

# cp /usr/local/etc/fail2ban/jail.conf /usr/local/etc/fail2ban/jail.old
# nano /usr/local/etc/fail2ban/jail.conf

Добавляем в самом низу файла jail.conf:
[owncloud]
enabled = true
filter = owncloud
port = https
logpath = /var/log/owncloud-acces.log   // путь к файлу логов ownCloud, который указывали на шаге 4.2
ignoreip = 192.168.1.59   // здесь указываются ip-адреса которые банится не будут
maxretry = 2   // количество неудачных попыток до бана
bantime = 86400   // время бана в секундах
findtime = 600   // время в течение которого введено необходимое для бана к-во попыток в секундах
action = bsd-ipfw   // указываем какие файлы действий выполнять
             pushover-notify   // описание данного файла действий будет ниже по тексту статьи, вводить здесь название данного действия необходимо с новой строки и после нажатия клавиши Tab.

4.5 Проверяем работает ли наш фильтр и может ли он найти в логе ownCloud нужные строки с попытками неудачного входа:

# fail2ban-regex /var/log/owncloud-acces.log /usr/local/etc/fail2ban/filter.d/owncloud.conf

Если всё верно, то в низу выдачи будет строка вида:

Lines: 2 lines, 0 ignored, 2 matches, 0 missed [processed in 0.0 sec]

4.6 Настраиваем действия, которые будут производится в случае неудавшихся попыток входа:

# cp  /usr/local/etc/fail2ban/action.d/bsd-ipfw.conf /usr/local/etc/fail2ban/action.d/bsd-ipfw.local
# nano /usr/local/etc/fail2ban/action.d/bsd-ipfw.conf

Оставляем в нём всё по умолчанию. В нём уже прописано правило, что при отправке в бан, ip-адрес добавляется в table(1) файервола ipfw:

actionban = ipfw table <table> add <ip>

Добавляем в сам файервол ipfw правило, блокирующее все ip-адреса, находящиеся в таблице table(1), т.к. пока нет никаких правил у файервола что делать с адресами из этой нашей таблицы(1):

# ipfw add 1 deny all from table(1) to me

Немного примеров по работе с ipfw:
ipfw list                                                  //Просмотреть имеющиеся правила
ipfw delete 13                                        //Удалить правило 13
ipfw add 14 <правило>                         //Добавить правило 14
ipfw table 1 add 192.168.1.5               //добавление в таблицу
ipfw table 1 add 192.168.1.0/24             //добавление в таблицу подсеть
ipfw table 1 list                                    //посмотреть что в таблице
ipfw add deny ip from table(10) to me      //Всё с таблицы 50 ко мне
ipfw table 1 delete 192.168.1.5              //удаляем из таблицы
ipfw table 1 flush                                  //чистим всю таблицу

4.7 Запускаем fail2ban:

Перед запуском создадим для fail2ban файл описывающий действие pushover-notify, о котором была уже речь выше и о котором мы ещё поговорим:

#touch /usr/local/etc/fail2ban/action.d/pushover-notify.conf

Прописываем автостарт fail2ban в /etc/rc.conf:

# sysrc fail2ban_enable="YES"

И запускаем его:

# /usr/local/etc/rc.d/fail2ban start

Если всё сделали правильно, то он запустится, если нет — разбирайтесь где ошибка. Если запустился, то проверяем на бан: заходим со стороннего ip-адреса с неправильным паролем. Должно забанить на время, которое мы указали в файле jail.conf.

Немного примеров по работе с fail2ban, которые могут пригодится в процессе отладки:
fail2ban-client status   // Посмотреть какие джейлы активны
fail2ban-client status owncloud   // Посмотреть статус конкретного джейла, где owncloud - это имя нашего джейла
fail2ban-client set owncloud unbanip MYIP   // Удалить конкретный ip-адрес из бана, где MYIP - этот самый ip-адрес

Собственно теперь у нас есть ownCloud, работающий на «взрослой» базе данных по https с защитой от подбора паролей.
Почти всё, но давайте ещё добавим уведомление о блокировке при неправильном вводе пароля в виде push-уведомлений на наш телефон.

5 Уведомление о блокировке ip-адреса

Для push-уведомлений будем использовать сервис pushover.net. Я думаю Вам теперь не составит труда разобраться с его API. Но если возникнут сложности, то пишите в комментариях и я добавлю соотвествующее описание по работе с данным сервисом.

5.1 Настраиваем уведомления pushover о неудачных попытках входа и бане:

# nano /usr/local/etc/fail2ban/action.d/pushover-notify.conf

В файле прописываем следующее:
[Definition]
actionstart=
actionstop=
actioncheck=
actionban = url -k https://api.pushover.net/1/messages.json -F token=<token> -F user=<user> -F title="ownCloud Alarm" -F message="<ip> is banned after <failures> attemts against <name>"
actionunban = url -k https://api.pushover.net/1/messages.json -F token=<token> -F user=<user> -F title="ownCloud Alarm" -F message="<ip> is unbanned"

[Init]
name = default
token =  [API Token/key (application key)]
user = [User key]

, где [API Token/key (application key)] и [User key] соответствующие значения с сайта pushover.net.
Перезапускаем fail2ban

# /usr/local/etc/rc.d/fail2ban restart

Проверяем работу уведомлений выполнив несколько неудачных попыток входа в ownCloud:

image

Вот и всё. Не забываем пробросить на роутере порты 80 и 443 для доступа к ownCloud.
И да, для большей безопасности можно заменить стандартные порты на что-то более экзотическое.

Автор: ACooper

Источник

Поделиться новостью

* - обязательные к заполнению поля