Мониторинг сайтов с помощью Monit

В предыдущей статье я описал как установить и настроить Monit для мониторинга сервера, но помимо мониторинга параметров сервера Monit можно использовать и для многих других целей, в частности мониторинга web-сайтов.

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

1. Доступность сайта — будем проверять разными методами:
1.1 Проверка по протоколу ICMP — как правило такая проверка не несет полезной информации, но помогает в случае возникновения проблем на сетевом уровне, например ваш хостинг-провайдер выключил сервер или у него возникли проблемы в сетевой инфраструктуре;
1.2 Проверка подключения на 80 или 443 порт web-сервера — это просто проверка подключения на данные порты, в первую очередь она показывает жив ли наш вебсервер и принимает ли он подключения на указанные порты, а то вдруг наш хостинг-провайдер или новоиспеченный системный администратор закрыли порты на фаерволе;
1.3 Проверка кода ответа web-сервера — это не что иное как проверка доступности сайта по протоколу HTTP методами HEAD, GET;
1.4 Проверка контента на сайте — аналогично п.1.3, но помимо того что мы просто проверяем код ответа сервера, мы еще дополнительно анализирует наличие ключевых слов на странице;
2. Проверка доступности и работоспособности базы данных сайта;
3. Проверка истечение срока регистрации доменного имени;
4. Проверка SSL сертификата, срока его действия, издателя;

Исходные данные: Наш сайт mysite.ru, сайт открывается как по http, так и по https

Проверка доступности сайта по протоколу ICMP

Такой метод проверки несет мало полезной информации, но он жизненно необходим для диагностики доступности сайта на сетевом уровне. И если проверка по ICMP выдает сбой, то это первый признак того, что с вашим сайтом, вернее с сервером где Ваш сайт что-то не так.

Создадим файл конфигурации /etc/monit/conf.d/mysite

check host mysite.ru_ping with address mysite.ru
   group mysite
   if failed ping4 count 5 with timeout 10 seconds then alert

Здесь мы проверяем наш сайт по протоколу ICMPv4 отправляя ICMP Echo Request с таймаутом 10 секунд.
Более детально о всех опциях проверки можно почитать в официальной документации.

Проверку подключения на 80-й порт и на 443-й порт.

Не путайте эту проверку с проверкой по протоколу HTTP(S). Здесь мы просто проверяем доступность порта web-сервера, то есть открыт порт или нет, по сути это проверка работоспособности web-сервера.
Если к примеру предыдущая проверка по протоколу ICMP показывает что все хорошо, а данная проверка выдает сбой, то Вы сразу можете понять, что на сетевом уровне сервер доступен, но что-то случилось со службой web-сервера, возможно «упал» процесс apache2 или nginx или нагрузка на web-сервер такая высокая, что он перестал принимать сетевые подключения.

Пишем в файл /etc/monit/conf.d/mysite

check host mysite.ru_connect with address mysite.ru
   group mysite
   if failed port 80 protocol http and request / for 2 cycles then alert
   if failed port 443 protocol https and request / for 2 cycles then alert

Более детально о всех опциях проверки можно почитать в официальной документации.

Проверка SSL сертификата нашего сайта

Данная проверка является модифицированным аналогом предыдущего варианта проверки на подключение.

В файле /etc/monit/conf.d/mysite пишем:

check host mysite.ru_ssl with address mysite.ru
   group mysite
   if failed port 443 protocol https and certificate valid > 7 days then alert

Здесь все просто, мы создали новую проверку дав ей имя mysite.ru_ssl и проверяем срок действия ssl сертификата, если до окончания срока действия осталось менее 7 дней нам будет выслано оповещение.

Так же мы можем проверять контрольную сумму сертификата, что очень актуально если у Вас постоянный сертификат выданный на 1 или 2 года, дополнительная проверка сертификата по контрольной сумме гарантирует, что это точно Ваш сертификат, а не подделка.

Проверку по контрольной сумме можно сделать так:

Вначале посмотрим SHA1 fingerprint нашего SSL сертификата (посмотреть можно 2-мя способами), на самом web-сервере:

openssl x509 -fingerprint -sha1 -in /etc/letsencrypt/live/mysite.ru/cert.pem | head -1 | cut -f2 -d'=' | sed 's,:,,g'

или на удаленном сервере:

openssl s_client -servername mysite.ru -connect mysite.ru:443 </dev/null 2>/dev/null | openssl x509 -text | sed -n -e '/BEGIN\ CERTIFICATE/,/END\ CERTIFICATE/p' | openssl x509 -fingerprint -sha1 | head -1 | cut -f2 -d'=' | sed 's,:,,g'

В файле /etc/monit/conf.d/mysite пишем:

check host mysite.ru_ssl_checksum with address mysite.ru
   group mysite
   if failed port 443 protocol https and certificate checksum sha1 = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" then alert

Мы описали 4 проверки которые имеют каждая свое имя (mysite.ru_ping, mysite.ru_connect, mysite.ru_ssl, mysite.ru_ssl_checksum). На мой взгляд такой подход более более правильный, т.к. позволяет группировать проверки по разным группам и управлять оповещениями более гибко и индивидуально. Если есть желание, то все 4 проверки можно объединить в одну, так:

check host mysite.ru with address mysite.ru
   group mysite
   if failed ping4 count 5 with timeout 10 seconds then alert
   if failed port 80 protocol http and request / for 2 cycles then alert
   if failed port 443 protocol https and request / for 2 cycles then alert
   if failed port 443 protocol https and certificate valid > 7 days then alert
   if failed port 443 protocol https and certificate checksum sha1 = "02F0B3DFAF013556A0305D7815B7C605F61E3787" then alert

Теперь давайте проверим конфигурацию monit, сделаем перезагрузку настроек и посмотрим результаты проверки.

# monit -t
Control file syntax OK

# monit reload
Reinitializing monit daemon

# monit -g mysite summary
Monit 5.24.0 uptime: 25m
┌─────────────────────────────────┬────────────────────────────┬───────────────┐
│ Service Name                    │ Status                     │ Type          │
├─────────────────────────────────┼────────────────────────────┼───────────────┤
│ mysite.ru_ping                  │ OK                         │ Remote Host   │
├─────────────────────────────────┼────────────────────────────┼───────────────┤
│ mysite.ru_connect               │ OK                         │ Remote Host   │
├─────────────────────────────────┼────────────────────────────┼───────────────┤
│ mysite.ru_ssl                   │ OK                         │ Remote Host   │
├─────────────────────────────────┼────────────────────────────┼───────────────┤
│ mysite.ru_ssl_checksum          │ OK                         │ Remote Host   │
└─────────────────────────────────┴────────────────────────────┴───────────────┘

# monit -g mysite status
Monit 5.24.0 uptime: 5h 14m

Remote Host 'mysite.ru_ping'
  status                       OK
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  ping response time           33.014 ms
  data collected               Tue, 10 Oct 2017 12:11:35

Remote Host 'mysite.ru_connect'
  status                       OK
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  port response time           258.556 m to mysite.ru:443/ type TCP/IP using TLS (certificate valid for 9 days) protocol HTTP
  port response time           174.378 m to mysite.ru:80/ type TCP/IP protocol HTTP
  data collected               Tue, 10 Oct 2017 12:11:35

Remote Host 'mysite.ru_ssl'
  status                       OK
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  port response time           261.374 m to mysite.ru:443 type TCP/IP using TLS (certificate valid for 9 days) protocol HTTP
  data collected               Tue, 10 Oct 2017 12:11:35

Remote Host 'mysite.ru_ssl_checksum'
  status                       OK
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  port response time           259.374 m to mysite.ru:443 type TCP/IP using TLS (certificate valid for 9 days) protocol HTTP
  data collected               Tue, 10 Oct 2017 12:11:36

Как видим, все отлично, все проверки успешно отработали. Наш SSL сертификат валиден еще 9 дней.

Давайте поменяем параметр проверки валидации SSL сертификата c 7 на 10 дней и перезапустим проверку и выведем результат:

# monit -t
Control file syntax OK

# monit reload
Reinitializing monit daemon

# monit status mysite.ru_ssl
Monit 5.24.0 uptime: 31m

Remote Host 'mysite.ru_ssl'
  status                       Timestamp failed
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  port response time           255.184 m to mysite.ru:443 type TCP/IP using TLS (certificate valid for 9 days) protocol HTTP
  data collected               Tue, 10 Oct 2017 12:17:48

Сейчас мы видим что Monit выдает статус «Timestamp failed» и на электронную почту пришло оповещение о том, что у нас сработала проверка валидации срока действия SSL сертификата.

Проверка кода ответа web-сервера, проверка отдачи контента на определенные ключевые слова.

Зачем вообще нужны такие проверки?

Все просто.

Проверка кода ответа web-сервер говорит о его корректной работе, при нормальной работе web-сервер должен отдавать HTTP код 200 для конкретной конечной страницы.

А зачем тогда проверять контент на предмет каких-то ключевых слов?

А контент нужно проверять чтобы:

1. Убедиться, что это именно Ваш сайт с Вашим контентом, а не какой-то подставной домен. Например если Вы забыли продлить срок регистрации Вашего домена, то регистратор снимает делегирование домена и отдает страницу-заглушку, на которой пишет, что срок регистрации домена закончился. Код ответа web-сервера регистратора будет 200 и предыдущая проверка не выявит ошибку, но вот проверка контента на уникальные слова выявит ошибку и вышлет Вам оповещение.

2. Дополнительная защита от кражи Вашего домена. К примеру злоумышленники могут получить доступ в управлению DNS записями Вашего домена и поменять там A-запись (IP адрес) на свой подставной сервер, поднять на этом сервере полную визуальную копию Вашего сайта, подменить на сайте Ваш email на свои и осуществлять какие-либо мошеннические действия от Вашего имени. От такого рода атаки поможет анализ контента Вашего сайта, Вы можете проверять наличие вашего email или номера телефона на нужных страницах и если их нет, то бить тревогу.
Можно еще усилить защиту сайта от кражи путем создания визуальной копии — это настроить web-сервер на отдачу специальных уникальных HTTP заголовков алгоритм формирования которых известен только Вам и даже если злоумышленники сделают полную визуальную копию Вашего сайта, даже сохранив уникальный контент который Вы проверяете, они не смогут разгадать алгоритм генерации уникальных HTTP заголовков, ведь чтобы его понять нужно взломать Ваш сервер и посмотреть логику создания этих заголовков.

Все эти проверки мы рассмотрим ниже.

Проверка кода ответа Web-сервера.

Здесь все предельно просто, проверяем код ответа на главной странице сайта, если он отличен от 200 — высылаем оповещение.

В файле /etc/monit/conf.d/mysite пишем:

check host mysite.ru_http with address mysite.ru
   group mysite
   if failed port 80 protocol http and request / status = 200 for 2 cycles then alert

Проверка контента на сайте.

Будем искать номер телефона на главной странице сайта.

В файле /etc/monit/conf.d/mysite пишем:

check host mysite.ru_content_tel with address mysite.ru
   group mysite
   if failed url http://mysite.ru with content = "XXX-XX-XX" for 2 cycles then alert

Проверим статус:

# monit status mysite.ru_content_tel
Monit 5.24.0 uptime: 1h 29m

Remote Host 'mysite.ru_content_tel'
  status                       OK
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  port response time           777.248 m to mysite.ru:80/ type TCP/IP protocol HTTP
  data collected               Tue, 10 Oct 2017 13:15:03

Все нормально. А теперь поменяем на сайте номер телефона и подождем очередную проверку.

Снова проверяем:

# monit status mysite.ru_content_tel
Monit 5.24.0 uptime: 1h 31m

Remote Host 'mysite.ru_content_tel'
  status                       Connection failed
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  port response time           FAILED to [mysite.ru]:80/ type TCP/IP protocol HTTP
  data collected               Tue, 10 Oct 2017 13:17:03

У нас есть аларм в консоле, а так же пришло оповещение на email такого вида:

Connection failed Service mysite.ru_content_tel 

	Date:        Tue, 10 Oct 2017 13:17:03
	Action:      alert
	Host:        myserver.ru
	Description: failed protocol test [HTTP] at [mysite.ru]:80/ [TCP/IP] -- HTTP error: Regular expression doesn't match: No match

Your faithful employee,
Monit

Теперь придумаем уникальный HTTP-заголовок который будет отдавать наш web-сервер и попробуем написать проверку этого заголовка.

У меня на сервере используется nginx, поэтому я напишу создание заголовка для него.
Заголовок будет X-Monit со статическим значением my_private_string

server {
    server_name mysite.ru;
    ....
    add_header X-Monit "my_private_string";
    ....
}

Теперь проверим наличие нашего заголовка в ответе сервера:

# curl -D - -o /dev/null -s mysite.ru
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 10 Oct 2017 08:33:48 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Last-Modified: Mon, 09 Oct 2017 10:02:36 GMT
ETag: "514a52f150a79ffcede3aa4aefe777a7"
Cache-Control: private, max-age=0
X-Monit: my_private_string

Все есть, пишем проверку для Monit.

К сожалению monit не умеет проверять HTTP заголовки и поэтому нам придется написать простенький скрипт на shell, который проверяет нужные нам заголовки и выдает результат, пишем:

Создаем файл /usr/sbin/monit_check_mysite_header.sh такого вида:

#!/usr/bin/env bash

mysite="http://mysite.ru"
myheader="X-Monit"
mystring="my_private_string"

shopt -s extglob
while IFS=':' read key value; do
        value=${value##+([[:space:]])}; value=${value%%+([[:space:]])}
        case "$key" in
                ${myheader}) myresult="$value"
                ;;
        esac
done < <(curl -sI ${mysite})

if [ -z ${myresult} ]; then
        echo "X-Monit-Empty"
        exit 2
else
       if [ ${myresult} == ${mystring} ]; then
               echo "OK"
       else
               echo "X-Monit-Wrong"
               exit 2
       fi
fi

Не забываем сделать файл исполняемым

chmod a+x /usr/sbin/monit_check_mysite_header.sh
chown root:root /usr/sbin/monit_check_mysite_header.sh

В файле /etc/monit/conf.d/mysite пишем:

check program mysite.ru_content_x_monit with path "/usr/sbin/monit_check_mysite_header.sh"
   group mysite
   if status != 0 then alert

Проверяем конфигурацию и перезагружаем её, потом ждем результатов проверки и смотрим:

# monit -t
Control file syntax OK

# monit reload
Reinitializing monit daemon

# monit status mysite.ru_content_x_monit
Monit 5.24.0 uptime: 2h 41m

Program 'mysite.ru_content_x_monit'
  status                       OK
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  last exit value              0
  last output                  OK
  data collected               Tue, 10 Oct 2017 14:27:42

Все есть, нужны нам заголовок присутствует.

А теперь давайте изменим содержимое нашего заголовка в nginx и подождем.

# monit status mysite.ru_content_x_monit
Monit 5.24.0 uptime: 2h 49m

Program 'mysite.ru_content_x_monit'
  status                       Status failed
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  last exit value              2
  last output                  X-Monit-Wrong
  data collected               Tue, 10 Oct 2017 14:36:00

Мы видим last output = X-Monit-Wrong, то есть наш заголовок стал не совпадать с тем что нужно.
Так же нам на почту пришло уведомление об этом:

Status failed Service mysite.ru_content_x_monit 

	Date:        Tue, 10 Oct 2017 14:35:56
	Action:      alert
	Host:        myserver.ru
	Description: status failed (2) -- X-Monit-Wrong

Your faithful employee,
Monit

Можно вообще убрать наш секретный заголовок X-Monit в nginx и тогда при следующей проверке в Monit будет статус X-Monit-Empty, то есть заголовка нет вообще.

Конечно заголовок должен быть не статическим как сделал я для примера, а динамическим и формироваться по определенному секретному алгоритму и тогда без вскрытия настроек web-сервера злоумышленники никогда не смогут подделать его и даже если они взломают ftp аккаунт сервера и БД сервера и скопируют их содержимое чтобы запустить абсолютно полную копию Вашего сайта, то Ваша проверка тут же это выявит по отсутствию секретного заголовка.

Проверка доступности и работоспособности базы данных сайта.

Данный вид простой проверки мы реализует с помощью скрипта на php, который будет лежать в определенной папке сайта, скрипт делает подключение в БД MySQL сайта, если все хорошо, то выдает OK, если что-то не так, то ERR. Monit периодически будет делать запрос к этому скрипту и проверку контента. Конечно такой вид проверки очень далек от идеала и есть масса ограничений в его работе, например если «упадет» web-сервер то и проверка БД выдаст ошибку или если «зависнет» процесс php-fpm, то проверка тоже потерпит фиаско, но вот если «упадет» БД на сервере, то тут скрипт однозначно скажет об этом.

Скрипт не выполняет никаких проверочных SQL запросов к БД, он просто подключается к MySQL и выбирает нужную БД. Вы можете изменить скрипт так, чтобы он делал простой SQL-запрос, например выводил количество записей в какой-либо табличке, это даст дополнительную уверенность, что в БД есть какие-либо данные и она не пустая.

Итак, сам php скрипт будет таким (расположим его в корне сайта с именем mysqlcheck.php):

<?php
$host='localhost';
$port='3306';
$user='debian-sys-maint';
$password='XXXXXXXX';
$db_name='mysite';
$link = @mysql_connect($host.($port!='' ? ':'.$port : ''), $user, $password);
if (!$link) die(mysql_error());
if ($db_name!='') {
  $db_selected = @mysql_select_db($db_name, $link);
  if (!$db_selected) die(mysql_error());
}
echo 'OK';
mysql_close($link);
?>

В файле /etc/monit/conf.d/mysite пишем:

check host mysite.ru_mysql with address mysite.ru
   group mysite
   if failed url http://mysite.ru/mysqlcheck.php and content == "OK" timeout 2 seconds 3 cycles then alert

Проверяем конфигурацию и перезагружаем её, потом ждем результатов проверки и смотрим:

# monit -t
Control file syntax OK

# monit reload
Reinitializing monit daemon

# monit status mysite.ru_mysql
Monit 5.24.0 uptime: 2h 41m

Remote Host 'mysite.ru_mysql'
  status                       OK
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  port response time           88.695 ms to mysite.ru:80/mysqlcheck.php type TCP/IP protocol HTTP
  data collected               Tue, 10 Oct 2017 15:21:56

Все отлично. Если хотите удостовериться в работе скрипта, то остановите БД MySQL и при очередной проверке Вы получите уведомление на email «HTTP error: Regular expression doesn’t match: No match».

Проверка истечение срока регистрации доменного имени.

Данный вид проверки так же не решается силами одного Monit, для проверки нам понадобиться сторонний скрипт на shell, но с ним все гораздо проще, он уже есть в моем репозитарии, остается только скачать его и написать небольшой shell-скрипт для анализа результата работы domain-check.pl.

wget https://raw.githubusercontent.com/CHERTS/linux-scripts/master/dns/domain-check/domain-check.pl -O /usr/sbin/domain-check.pl
chmod a+x /usr/sbin/domain-check.pl
chown root:root /usr/sbin/domain-check.pl

Пишем простой скрипт для monit для проверки истечение срока регистрации доменного имени:

Сохраняем его в /usr/sbin/monit_check_mysite_expire.sh

#!/usr/bin/env bash

domain="mysite.ru"
interval="10"

domain_days_left=$(domain-check.pl -d=${domain} | grep ${domain} | sed '1,1d' | awk -F'/' '{print $2}' | sed 's, ,,g')

if [ ${domain_days_left} -lt ${interval} ]; then
        echo "Registration of domain ${domain} will expire in ${domain_days_left} days."
        exit 2;
elif [ ${domain_days_left} -eq 0 ]; then
        echo "Registration of the domain ${domain} has expired."
        exit 2;
else
        echo "OK"
fi
exit 0;

Не забываем сделать файл исполняемым

chmod a+x /usr/sbin/monit_check_mysite_expire.sh
chown root:root /usr/sbin/monit_check_mysite_expire.sh

В файле /etc/monit/conf.d/mysite пишем:

check program mysite.ru_domain with path "/usr/sbin/monit_check_mysite_expire.sh"
   group mysite
   if status != 0 then alert

Проверяем конфигурацию и перезагружаем её, потом ждем результатов проверки и смотрим:

# monit -t
Control file syntax OK

# monit reload
Reinitializing monit daemon

# monit status mysite.ru_domain
Monit 5.24.0 uptime: 2h 41m

Remote Host 'mysite.ru_domain'
  status                       OK
  monitoring status            Monitored
  monitoring mode              active
  on reboot                    start
  last exit value              0
  last output                  OK
  data collected               Tue, 10 Oct 2017 16:48:02

Все отлично. Проверка работает.

Чтобы убедиться в работоспособности измените в скрипте /usr/sbin/monit_check_mysite_expire.sh параметр interval с 10 до 1000 и подождите, при следующей проверке monit выдаст оповещение.

И в заключении я приведу всю конфигурацию для мониторинга сайта.

Это наш файл /etc/monit/conf.d/mysite который получился по результатам статьи:

check host mysite.ru_ping with address mysite.ru
   group mysite
   if failed ping4 count 5 with timeout 10 seconds then alert

check host mysite.ru_connect with address mysite.ru
   group mysite
   if failed port 80 protocol http and request / for 2 cycles then alert
   if failed port 443 protocol https and request / for 2 cycles then alert

check host mysite.ru_http with address mysite.ru
   group mysite
   if failed port 80 protocol http and request / status = 200 for 2 cycles then alert

check host mysite.ru_ssl with address mysite.ru
   group mysite
   if failed port 443 protocol https and certificate valid > 7 days then alert

check host mysite.ru_ssl_checksum with address mysite.ru
   group mysite
   if failed port 443 protocol https and certificate checksum sha1 = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" then alert

check host mysite.ru_content_tel with address mysite.ru
   group mysite
   if failed url http://mysite.ru with content = "XXX-XX-XX" for 2 cycles then alert

check program mysite.ru_content_x_monit with path "/usr/sbin/monit_check_mysite_header.sh"
   group mysite
   if status != 0 then alert

check host mysite.ru_mysql with address mysite.ru
   group mysite
   if failed url http://mysite.ru/mysqlcheck.php and content == "OK" timeout 2 seconds 3 cycles then alert

check program mysite.ru_domain with path "/usr/sbin/monit_check_mysite_expire.sh"
   group mysite
   if status != 0 then alert

Мы рассмотрели вопрос мониторинга работоспособности сайта с помощью системы Monit, данные виды проверок позволяют достаточно эффективно и быстро диагностировать перебои в работе сайта, а так же позволяют заблаговременно оповестить владельца сайта о наступлении возможных проблем.

Если у Вас возникли вопросы или Вы хотите чтобы я помог Вам настроить мониторинг Вашего сайта, то Вы всегда можете оставить комментарий в этой статье или написать мне на email: sleuthhound [AT] gmail [DOT] com


Один комментарий

  • Ответить Сергей |

    Очень полезная статья.
    Как раз занимался вопросом мониторинга сайтов с помощью Monit, а тут такая помощь!

Хотите оставить комментарий?