UMGUM.COM 

Bacula + Web ( Налаживаем отображение сведений об использовании "Bacula" ресурсов, включая детали статусов заданий и томов данных, посредством web-интерфейса. )

12 февраля 2019

OS: "Linux Debian 9 (Stretch)".
Application: Bacula, Nginx, PHP-FPM, "Bacula-Web v8.2.1".

Задача: наладить отображение сведений об использовании ресурсов системой резервного копирования "Bacula", включая детальные данные о статусе заданий и томов данных, посредством web-интерфейса.

"Bacula" довольно таки специфичный продукт, слегка подзастрявший где-то между любительской разработкой энтузиастов командной строки Linux и системы корпоративного уровня, отчего в базовой поставке всё управление реализовано через конфигурационные файлы и специализированную CLI-консоль. Графических интерфейсов почти нет, они зачаточны или предоставляются только для коммерческих поставок, как "BWeb Management Suite", например. Среди бесплатных вариантов на мой взгляд выбор ограничен двумя: "Bacula-Web" и "Webacula". Первый продукт приятнее, но умеет только собирать статистику. Второй корявее, но посредством его web-инерфейса возможен запуск задач восстановления. Продвигаемый самими разработчиками системы резервного копирования "Baculum" на мой вкус стилистически убог неприемлемо. Остальные проекты мелкие и малофункциональные.

Я использую в работе "Bacula-Web" - инструмент исключительно для отображения статистической информации, абсолютно без возможности воздействия на конфигурацию сервиса резервного копирования:

размер: 320 400 640 800 1024 1280
Bacula-Web: пример интерфейса web-сервиса отображения состояния системы резервного копирования "Bacula".
1570x876 • Bacula-Web: пример интерфейса web-сервиса отображения состояния системы резервного копирования "Bacula".

Я знаю, что "Webacula" поддерживает возможность запуска задач, в том числе и типа "Restore", посредством web-интерфейса - но моя принципиальная позиция в этом вопросе сводится к тому, что процедуры извлечения и восстановления данных имеют комплексный характер, непростые ввиду необходимости учитывать взаимосвязи нескольких сервисов и должны исполнятся специалистом своего дела, отлично владеющим CLI-инструментарием "Bacula".

Естественно, система резервного копирования как таковая должна быть уже установлена, сконфигурирована и успешно работать. Далее разворачиваем web-сервис и web-приложение.


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

# apt-get install aptitude sudo acl psmisc host telnet htop iotop mc pwgen

Предварительная настройка СУБД MySQL.

Прежде всего подключимся к СУБД и создадим пользователя "баз данных" системы резервного копирования, который будет иметь доступ только на чтение - для сбора статистики этого достаточно:

# mysql
mysql> CREATE USER 'web_bacula'@'localhost' IDENTIFIED BY 'strongROUserPassword';
mysql> GRANT SELECT,SHOW VIEW ON bacula.* TO 'web_bacula'@'localhost';
mysql> FLUSH PRIVILEGES;

Предварительная настройка СУБД PostgreSQL.

Мы везде используем "PostgreSQL v9+", для работы с которой придётся предпринять чуть больше действий.

Подключаемся к СУБД и заводим нового пользователя (здесь он называется "ролью"):

# sudo -u postgres psql
postgres=# CREATE ROLE web_bacula WITH LOGIN PASSWORD 'strongROUserPassword';

Разрешаем подключаться к конкретной "базе данных":

postgres=# GRANT CONNECT ON DATABASE bacula TO web_bacula;

Разрешаем работать с таблицами каталога сущностей "public" внутри указанной БД, предоставляя доступ к таковым только на уровне выборки данных:

postgres=# \connect bacula
bacula=# GRANT USAGE ON SCHEMA public TO web_bacula;
bacula=# GRANT SELECT ON ALL TABLES IN SCHEMA public TO web_bacula;
bacula=# GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO web_bacula;

Проверяем доступность ресурса для созданного пользователя, инициируя соединение в контексте пользователя web-сервера:

# sudo -u www-data psql -h localhost -U web_bacula -W bacula

bacula-> \d+
                                     List of relations
Schema | Name                |   Type   |  Owner   |    Size    |
-------+---------------------+----------+----------+------------+
....
public | cdimages            | table    | postgres | 0 bytes    |
public | client              | table    | postgres | 48 kB      |
public | client_clientid_seq | sequence | postgres | 8192 bytes |
....
bacula-> \q

Подготовка файловой структуры для web-приложения.

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

# mkdir -p /var/www/bacula-web.example.net
# mkdir -p /var/www/bacula-web.example.net/tmp
# mkdir -p /var/www/bacula-web.example.net/www

Через расширение POSIX ACL предпишем устанавливать всем создаваемым файлам (и директориям) разрешения полного доступа как для пользователя, так и группы (в отличии от системных установок "umask 0022", разрешающим группе только чтение), при этом полностью убираем доступ всем остальным:

# setfacl --no-mask --set default:user::rwX,default:group::rwX,default:other:--- /var/www

Опция "--no-mask" в команде выше важна - она прямо указывает не использовать какие-либо фильтры при вычислении "итоговых допусков", устанавливая буквально только те, что указаны.

Профилактически закрываем доступ к директориям сайтов всем посторонним, передавая ресурсы во владение выделенному пользователю, от имени которого запускается web-сервер:

# chown -R www-data:www-data /var/www
# chmod -R ug+rw /var/www
# chmod -R o-rw /var/www

Настраиваем PHP.

Устанавливаем сам PHP-интерпретатор и набор модулей, необходимых для работы "Bacula-Web":

# aptitude install php-fpm php-cgi php-cli php-opcache php-pgsql php-mysqli php-sqlite3 php-mbstring php-gettext php-pclzip php-xml php-sockets php-json php-gd php-curl php-zip php-xmlrpc php-intl

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

# vi /etc/php/7.0/fpm/php.ini

....
date.timezone = Asia/Novosibirsk
....
cgi.fix_pathinfo = 0
pcre.jit = 0
....
mbstring.default_charset = UTF-8
mbstring.internal_encoding = UTF-8
mbstring.detect_order = "UTF-8"
mbstring.encoding_translation = on
mbstring.strict_detection = on
....
opcache.enable = 1
opcache.memory_consumption = 64
opcache.max_accelerated_files = 10000
opcache.validate_timestamps = 1
opcache.use_cwd = 1
opcache.revalidate_path = 1
opcache.revalidate_freq = 0
....

Заведём отдельный FPM-пул:

# vi /etc/php/7.0/fpm/pool.d/bacula-web.conf

; Блок описания отдельного инстанса PHP-FPM
[bacula-web]

; Указываем запускать инстанс от имени основного пользователя - web-сервера
user = www-data
group = www-data

; Задаём точку приёма FCGI-запросов от шлюза (Nginx в нашем случае)
listen = /var/run/php/fpm-bacula-web.sock

; Явно задаём владельца точки входа FCGI-запросов и разрешения для доступа к ней web-сервера
listen.owner = www-data
listen.group = www-data
listen.mode = 0600

; Режим запуска инстанса
pm = dynamic
; Количество процессов, запускаемых при старте PHP-FPM
pm.start_servers = 2
; Максимальное количество процессов, которые могут быть запущены для обработки запросов
pm.max_children = 48
; Параметры количества запущенных неактивных процессов (находящихся в ожидании запросов)
pm.min_spare_servers = 2
pm.max_spare_servers = 5
; Количество запросов, после которого процесс будет перезапущен (для компенсации "утечек памяти" в скриптах)
pm.max_requests = 4096

; Блок переопределения в среде исполнения PHP-FPM глобальных параметров PHP-интерпретатора
php_admin_value[sys_temp_dir] = /var/www/bacula-web.example.net/tmp
;php_admin_flag[allow_url_fopen] = On

Проверяем корректность основного и файла конфигурации пула:

# php -e -c /etc/php/7.0/fpm/php.ini -r 'echo "OK\n";';
# php-fpm7.0 -t --fpm-config /etc/php/7.0/fpm/pool.d/bacula-web.conf

Удаляем ненужную конфигурацию "по умолчанию" и перезапускаем сервис PHP-FPM:

# rm /etc/php/7.0/fpm/pool.d/www.conf
# /etc/init.d/php7.0-fpm restart

Настраиваем Nginx.

Устанавливаем пакет web-сервера, в базовой поставке, без изысков:

# aptitude install nginx

Для включения поддержки Nginx протокола HTTPv2 заранее генерируем DH-сертификат (считаем, что SSL-сертификаты уже имеются):

# mkdir -p /etc/nginx/ssl/
# openssl dhparam -out /etc/nginx/ssl/dhparam.2048.pem 2048
# chmod -R go-rwx /etc/nginx/ssl*

Слегка дополняем глобальную конфигурацию web-сервиса:

# vi /etc/nginx/nginx.conf

# Явно запускаем Nginx в рамках привилегий пользователя "www-data"
user www-data www-data;
....
worker_processes 8;
....
events {
  worker_connections 1024;
  ....
}

http {
  ....
  tcp_nodelay on;
  tcp_nopush on;

  # Запрещаем web-серверу сообщать о себе подробные данные
  server_tokens off;

  # Запрещаем просмотр содержимого директории, если не указан целевой файл
  autoindex off;

  # Отключаем проверку размера тела передаваемого PHP-FPM запроса
  client_max_body_size 0;

  # Увеличиваем размер блоков данных, которыми обмениваются Nginx и PHP-FPM
  client_body_buffer_size 4M;
....

Описываем конфигурацию нашего служебного web-сайта:

# vi /etc/nginx/sites-available/bacula-web.example.net.conf

# Перехватываем все запросы по протоколу без шифрования и перенаправляем их на HTTPS
server {
  listen 80 default_server;
  server_name bacula-web.example.net www.bacula-web.example.net;
  access_log off;
  error_log  off;
  location / {
    rewrite ^ https://bacula-web.example.net$request_uri permanent;
  }
}

# Перехватываем запросы к имени нежелательного формата и перенаправляем к нужному
server {
  listen      443 ssl http2;
  server_name www.bacula-web.example.net;

  access_log off;
  error_log  off;

  ssl on;
  ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_dhparam         /etc/nginx/ssl/dhparam.2048.pem;
  ssl_certificate     /etc/nginx/ssl/bacula-web.example.net.crt;
  ssl_certificate_key /etc/nginx/ssl/bacula-web.example.net.key.decrypt;
  ssl_session_cache shared:SSL:30m;
  ssl_session_timeout 1h;
  ssl_stapling on;
  add_header Strict-Transport-Security max-age=15768000;

  location / {
    rewrite ^ https://bacula-web.example.net$request_uri permanent;
  }
}

# Описывем рабочее окружение web-сайта как такового
server {
  listen      443 ssl http2 default_server;
  server_name bacula-web.example.net;

  access_log /var/log/nginx/bacula-web.example.net.access.log;
  error_log /var/log/nginx/bacula-web.example.net.error.log;

  # SSL Configuration
  ssl on;
  ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
  # Old backward compatibility (Windows XP IE6, Java 6)
  ssl_ciphers HIGH:SEED:AES128-SHA:AES256-SHA:DES-CBC3-SHA:RC4-SHA:RC4-MD5:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH;
  ssl_prefer_server_ciphers on;
  ssl_dhparam         /etc/nginx/ssl/dhparam.2048.pem;
  ssl_certificate     /etc/nginx/ssl/bacula-web.example.net.crt;
  ssl_certificate_key /etc/nginx/ssl/bacula-web.example.net.key.decrypt;
  # SSL Caching
  ssl_session_cache shared:SSL:30m;
  ssl_session_timeout 1h;
  # SSL Verify Optional
  ssl_stapling on;
  # SSL Strict Optional
  add_header Strict-Transport-Security max-age=15768000;

  # Выключаем невостребованную обычно перекодировку контента
  charset off;

  # Явно указываем корень файловой структуры сайта, выше которой web-сервер не должен выходить
  root /var/www/bacula-web.example.net/www;

  # Задаём переменную с многократно используемым параметром PHP-FPM
  set $php_pass unix:/run/php/fpm-bacula-web.sock;

  # Задаём перечень файлов, которые web-сервер должен выдать в отсутствии явного указания со стороны клиента
  index index.html index.htm index.php;

  # Обслуживаем только интранет-клиентов и только успешно прошедших HTTP-аутентификацию
  satisfy all;
  allow 10.0.0.0/8;
  allow 172.16.0.0/12;
  allow 192.168.0.0/16;
  deny  all;
  #
  auth_basic "Restricted";
  auth_basic_user_file $document_root/.htpasswd;

  # Глобальный обработчик событий отсутствия запрашиваемого файла
  location / {
    try_files $uri $uri/ =404;
  }

  # Закрываем доступ к совершенно нефункциональной "административной панели"
  if ( $request_uri ~* page=settings ) {
    set $args "";
    rewrite ^ $scheme://$host permanent;
  }

  # Блокируем доступ извне к внутренностям "Bacula-Web"
  location ~* (/application/config|/application/assets/protected|/application/views/cache|bwc|console.php|composer) {
    deny all;
    log_not_found off;
    access_log off;
  }

  # Блокируем доступ к типовым "закрытым" ресурсам
  location ~* (/\.ht|/\.hg|/\.svn|/\.git|/\.enabled|/\.config) {
    deny all;
    log_not_found off;
    access_log off;
  }

  # Блокируем доступ к классически неправильно и опасно именованным файлам (вроде ".php.1")
  location ~* \.(phtml|php)(?!(\?|\/|$)) {
    deny all;
    log_not_found off;
  }

  # Обработчик прямых обращений к PHP-скриптам
  location ~* \.(phtml|php)$ {
    try_files     $uri =404;
    include       /etc/nginx/fastcgi_params;
    fastcgi_pass  $php_pass;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  }

  # Напрямую отдаём "статические" данные, предлагая браузеру сохранить их в своём "кеше", и не фиксируем эти события
  location ~* ^.+\.(jpg|jpeg|gif|png|svg|js|css|mp3|ogg|mpe?g|avi|zip|gz|bz2?|rar|swf|xml|txt)$ {
    try_files   $uri =404;
    expires     30d;
    access_log  off;
  }
}

Активируем конфигурацию:

# rm /etc/nginx/sites-enabled/default
# ln -s /etc/nginx/sites-available/bacula-web.example.net.conf /etc/nginx/sites-enabled/bacula-web.example.net.conf

Проверяем синтаксическую корректность новой конфигурации и указываем её принять:

# nginx -t
# /etc/init.d/nginx reload

Настраиваем аутентификацию.

Разработчиком "Bacula-Web" заявлено, что начиная с v8 его web-приложение поддерживает самостоятельную аутентификацию пользователей, логины и пароли которых сохраняются в БД "SQLite" (./application/assets/protected/application.db). Однако, по состоянию на Январь 2019 этот функционал реализован откровенно слабо, контролю через web-интерфейс в общепринятом смысле не поддаётся и я остаюсь на HTTP-аутентификации посредством web-сервера, а не приложения.

Подсистема "HTTP Basic Authentication" использует шифрование паролей на грани минимально допустимого с точки зрения устойчивости к взлому, но иногда приходится поддерживать совместимость со старыми протоколами, так что генерируем из желаемого пароля специфичного типа "хеш" (MD5-based password algorithm, Apache variant):

# openssl passwd -apr1 <password>

Полученный "хеш" подкладываем в файл с перечнем пользователей, доступ которым разрешается:

# vi /var/www/bacula-web.example.net/www/.htpasswd

# <userName>:<apache-MD5-Hash-of-userPassword>:<Comment>
userOne:$...:Joe Bloggs
....

Установка и настройка web-приложения "Bacula-Web".

Элементарно скачиваем дистрибутив web-приложения с сайта разработчика, забирая его по следующему адресу:


Распаковываем архив в директорию web-приложения:

# cd /var/www/bacula-web.example.net/www
# tar -xvf ./bacula-web-8.2.1.tgz
# mv ./bacula-web/* ./ && rm -rf ./bacula-web

Явно переводим файловые ресурсы во владение пользователя web-сервера:

# chown -R www-data:www-data /var/www/bacula-web.example.net/www
# chmod -R ug+rw /var/www/bacula-web.example.net/www
# chmod -R o-rw /var/www/bacula-web.example.net/www

Все настройки "Bacula-Web" сосредоточены в одном конфигурационном файле - правим определяющие параметры:

$ mv ./application/config/config.php.sample ./application/config/config.php
$ vi ./application/config/config.php

<?php
....

// Отключаем встроенную в приложение поддержку аутентификации
$config['enable_users_auth'] = false;
....

// Параметры подключения к СУБД PostgreSQL
$config[0]['label'] = 'bacula.example.net';
$config[0]['host'] = 'localhost';
$config[0]['login'] = 'web_bacula';
$config[0]['password'] = 'strongROUserPassword';
$config[0]['db_name'] = 'bacula';
$config[0]['db_type'] = 'pgsql';
$config[0]['db_port'] = '5432';
....

Проверка корректности настройки web-сервиса осуществляются на специальной странице "https://bacula-web.example.net/index.php?page=test".


Заметки и комментарии к публикации:


Оставьте свой комментарий ( выразите мнение относительно публикации, поделитесь дополнительными сведениями или укажите на ошибку )