Мониторинг SSL-сертификатов в oVirt Engine: как мы научились спать спокойно благодаря Go и Prometheus

от автора

Авторы статьи: Артем Зубков, Junior администратор отдела DevOps.

В современных распределённых системах надёжность и безопасность инфраструктуры напрямую зависят от корректного функционирования криптографических компонентов, в частности — SSL/TLS-сертификатов. Одним из критически важных аспектов эксплуатации таких систем является своевременный мониторинг срока действия сертификатов, поскольку их просрочка может привести к нарушению работы сервисов, недоступности API, сбоям в аутентификации и даже компрометации безопасности соединений. В рамках экосистемы oVirt Engine, выступающего центральным узлом управления виртуальной инфраструктурой, особое значение имеют сертификаты, обеспечивающие защищённое взаимодействие между компонентами системы и конечными пользователями.

Серверы для мониторинга

Закажите сервер с предустановленным ПО для мониторинга: Grafana, Zabbix, Prometheus и другими.

Узнать больше

В данной статье мы рассмотрим практику реализации автоматизированного мониторинга срока действия двух ключевых сертификатов: apache.cer и websocket-proxy.cer, размещённых на каждом oVirt Engine в директории /etc/pki/ovirt-engine/certs/

Сертификат apache.cer используется веб-сервером Apache, который обслуживает веб-интерфейс и REST API oVirt Engine, обеспечивая шифрование и аутентификацию клиентских подключений. В свою очередь, websocket-proxy.cer применяется для защиты WebSocket-соединений, необходимых для передачи консольных сессий виртуальных машин через браузер. Несвоевременное обновление этих сертификатов может привести к недоступности управления виртуальными машинами и административного интерфейса, что делает их мониторинг приоритетной задачей.

Для решения этой задачи мы разработали специализированный экспортер — cert_checker, размещаемый непосредственно на каждом oVirt Engine в каталоге /opt/cert_checker. Для тех, кто не знает, oVirt Engine — это центральный сервер управления, который контролирует все ноды виртуализации, общие дисковые ресурсы и виртуальные сети.

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

Для корректной работы экспортера требуется наличие на хосте установленной версии Go (golang) не ниже той, которая использовалась при его компиляции, что гарантирует стабильность и совместимость исполняемого файла. Далее в статье будет подробно описана архитектура решения, процесс развёртывания, формат выдаваемых метрик и рекомендации по интеграции в инфраструктуру мониторинга.

Создание сервиса

Для работы экспортера так же необходимо создать сервис systemd. Для это идем в каталог /etc/systemd/system/ и создаем systemd-unit:

Description=oVirt cert cheker service ConditionPathExists=/opt/cert_cheker After=network.target [Service] Type=simple WorkingDirectory=/opt/cert_cheker ExecStart=/usr/local/go/bin/go run main.go //указать путь установки go и способ выполнения main.go User=root ExecReload=/bin/kill -HUP $MAINPID ExecStop=/bin/kill $MAINPID KillSignal=SIGQUIT TimeoutStopSec=5 Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target

После чего сделать systemctl daemon-reload, systemctl start cert_cheker.service и проверить статус работы сервиса systemctl status cert_cheker.service. В случае успеха вывод статуса должен выглядеть примерно так:

И наконец, нужно убедиться, что нужный нам порт (порт 1337), куда будут отдаваться метрики, не закрыт фаерволлом.

Для этого нужно ввести, например, команду 

netstat -na | grep 1337

Если вывод имеет следующий вид.:

Тогда необходимо открыть порт вручную, введя команду

firewall-cmd --add-port 1337/tcp

Проверяем результат:

Если порт открыт, следует проверить, тянется ли метрика. Для этого нужно ввести новую команду

curl http://localhost:1337/metrics 

и получить вывод, похожий на тот, что изображен на скриншоте ниже:

Создание алерта Prometheus

Первым делом создаем алерт. Все примеры мы будем показывать ссылась на наш gitlab и расположение Ansible плейбуков для развертывания инфраструктуры в нем. В случае Prometheus, его алерты можно найти по по пути /devops/ansible-playbooks/prometheus_playbook/ files/alerts

Выбираем раздел меню New file и создаем файл в формате yml:

Затем пишем алерт, где указываем имя самого алерта, нужный для мониторинга атрибут + значение, на которое будет срабатывать алерт, время обновления, тип алерта (в нашем примере warning), а также описание. Формат следующий:

groups: - name: ovirt_engine_apache_cert_expiry   rules:     - alert: ovr_apache_cert_expiry       expr: ovirt_engine_apache_cert_expiry < 14       for: 45s       labels:         severity: warning       annotations:         summary: "Certificate for {{ $labels.engine }} will expire soon"         description: "The certificate for {{ $labels.engine }} will expire in {{ $value }} days. Please renew it." - name: ovirt_engine_ws_proxy_cert_expiry   rules:     - alert: ovr_ws_proxy_cert_expiry       expr: ovirt_engine_ws_proxy_cert_expiry < 14       for: 45s       labels:         severity: warning       annotations:         summary: "Certificate for {{ $labels.engine }} will expire soon"         description: "The certificate for {{ $labels.engine }} will expire in {{ $value }} days. Please renew it."

Сохраняем и переходим в каталог /devops/ansible-playbooks/prometheus_playbook/group_vars. Нас интересует файл federation_lang.yaml. Открываем его через web IDE и в верхней части документа, рядом с остальными таргетами, ниже создаем свой:

В таргете прописаны имя таргета, путь на каталог на HTTP, поднятом ранее на 1337 порту. Далее, после static_configs, прописываем целевые адреса за портом 1337. Затем, после labels, название сервиса и компонент virtualization.

Так же необходимо в самом начале документа, после поля rule_files указать имя написанного алерта:

После этого переходим к Jenkins задаче conf_prometheus.dsl и запускаем обновление fideration_lang:

После успешного обновления следует перейти по URL (http://<ip>:9090/) и проверить отработку таргета, введя имя искомого атрибута, например ovirt_engine_apache_cert_expiry. Если проблем не возникло, то вывод будет примерно таким:

Вывод алерта на дэшборд Grafana

Если предыдущие шаги выполнены успешно, алерт будет выведен на общий дэшборд. Необходимо перенести его на дэшборд oVirt. Делается это следующим образом:

Заходим в настройки панели, выбрав пункт меню Edit:

Вписываем название алерта в формате job!=»cert_cheker». Обязательно после должна быть запятая. Если название вписывается в середине поля, то запятую ставим с обеих сторон:

Далее идем в дэшборд Prometheus AlertManager — Ovirt checks:

На дэшборде oVirt снова заходим в настройке панели:

Жмем + Query :

В появившемся поле вписываем имя алерта опять в формате job=»cert_cheker».

После чего нажимаем Save в верхней части страницы:

Как следствие, после выполненных действий алерт будет выводиться на дэшборд oVirt, и настройку можно считать оконченной.

Описание работы кода экспортера

Для начала импортируются необходимые пакеты, включая crypto/x509 для работы с сертификатами.  Пакет crypto/x509 имеет ключевое значение, поскольку предоставляет функции для парсинга сертификатов X.509 и проверки срока действия сертификатов.

Также необходимы пакеты encoding/pem для декодирования PEM-закодированных данных, io для работы с вводом-выводом, log для логирования, net/http для работы с HTTP-запросами, os для работы с операционной системой и time для работы со временем.

Еще необходимо импортировать пакеты из внешних библиотек:

  • github.com/prometheus/client_golang/prometheus для работы с Prometheus;

  • github.com/prometheus/client_golang/prometheus/promhttp для обработки HTTP-запросов Prometheus:

package main import (     "crypto/x509"     "encoding/pem"     "io"     "log"     "net/http"     "os"     "time"     "github.com/prometheus/client_golang/prometheus"     "github.com/prometheus/client_golang/prometheus/promhttp" )

Дальше определяются две метрики: apacheCertExpiry и wsProxyCertExpiry, которые представляют собой измерители (gauges) в Prometheus. Они используются для отслеживания количества дней до истечения срока действия сертификатов Apache и WebSocket Proxy соответственно:

var (     apacheCertExpiry = prometheus.NewGauge(prometheus.GaugeOpts{         Name: "ovirt_engine_apache_cert_expiry",         Help: "Number of days until the Apache certificate expires",     })     wsProxyCertExpiry = prometheus.NewGauge(prometheus.GaugeOpts{         Name: "ovirt_engine_ws_proxy_cert_expiry",         Help: "Number of days until the WebSocket Proxy certificate expires",     }) )

В функции init() эти метрики регистрируются в Prometheus с помощью функции prometheus.MustRegister():

func init() {     prometheus.MustRegister(apacheCertExpiry)     prometheus.MustRegister(wsProxyCertExpiry) }

В функции main() запускается HTTP-сервер (в данном случае на порту 1337), который будет принимать метрики в каталог /metrics, в формате который поддерживает Prometheus:

func main() {     log.Println("Starting HTTP server on :1337")     http.Handle("/metrics", promhttp.Handler())     go func() {         log.Fatal(http.ListenAndServe(":1337", nil))     }()

Далее в бесконечном цикле каждый час проверяется срок действия сертификатов Apache и WebSocket Proxy с помощью функции checkCertExpiry(). Эта функция принимает путь к файлу сертификата в качестве аргумента и возвращает количество дней до истечения срока действия сертификата. Если количество дней до истечения срока действия сертификата больше или равно 0, значение метрики обновляется с помощью функции Set() :

for {         apacheDaysUntilExpiry := checkCertExpiry("/etc/pki/ovirt-engine/certs/apache.cer")         if apacheDaysUntilExpiry >= 0 {             apacheCertExpiry.Set(float64(apacheDaysUntilExpiry))         }         wsProxyDaysUntilExpiry := checkCertExpiry("/etc/pki/ovirt-engine/certs/websocket-proxy.cer")         if wsProxyDaysUntilExpiry >= 0 {             wsProxyCertExpiry.Set(float64(wsProxyDaysUntilExpiry))         }         time.Sleep(1 * time.Hour)     } }

Если срок действия сертификата не удалось определить, тогда возвращается -1:

func checkCertExpiry(certFile string) int {     log.Printf("Checking certificate %s\n", certFile)     file, err := os.Open(certFile)     if err != nil {         log.Printf("Failed to open certificate %s: %v", certFile, err)         return -1     }     defer file.Close()     certData, err := io.ReadAll(file)     if err != nil {         log.Printf("Failed to read certificate %s: %v", certFile, err)         return -1     }     block, _ := pem.Decode(certData)     if block == nil {         log.Printf("Failed to decode PEM block for %s", certFile)         return -1     }     cert, err := x509.ParseCertificate(block.Bytes)     if err != nil {         log.Printf("Failed to parse certificate %s: %v", certFile, err)         return -1     }     daysUntilExpiry := int(cert.NotAfter.Sub(time.Now()).Hours() / 24)     log.Printf("Certificate %s expires in %d days\n", certFile, daysUntilExpiry)     return daysUntilExpiry }

В функции checkCertExpiry() сначала открывается файл сертификата с помощью функции os.Open(). Потом данные сертификата читаются из файла с помощью функции io.ReadAll(). Далее данные сертификата декодируются из формата PEM с помощью функции `pem.Decode(). Затем данные сертификата парсятся в структуру x509.Certificate с помощью функции x509.ParseCertificate().

После этого вычисляется количество дней до истечения срока действия сертификата путем вычитания текущего времени из времени истечения срока действия сертификата с помощью функции Sub() и преобразования результата в дни. Количество дней до истечения срока действия сертификата возвращается функцией checkCertExpiry().

Итог

В ходе реализации мониторинга SSL/TLS-сертификатов в экосистеме oVirt мы создали надёжное и автоматизированное решение на основе самописного экспортера cert_checker. Этот инструмент позволяет в режиме реального времени отслеживать сроки истечения ключевых сертификатов — apache.cer и websocket-proxy.cer, — предотвращая потенциальные простои в работе веб-интерфейса и консольных подключений к виртуальным машинам. 

Интеграция с Prometheus и Grafana обеспечила нам централизованное наблюдение, а настройка алертинга позволила оперативно реагировать на приближающееся окончание срока действия сертификатов — за 14 дней до истечения, а не в последний момент. 

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

С учетом нескольких месяцев эксплуатации, пока «причесывалась» эта статья, можно констатировать следующее: решение оказалось простым, но эффективным: благодаря использованию стандартных библиотек Go и экосистемы Prometheus, мы получили гибкий и легко поддерживаемый компонент, который можно быстро адаптировать под другие типы сертификатов или системы. Важно, что теперь мониторинг стал проактивным — вместо реагирования на инциденты мы можем предотвращать их заранее.

А как вы организуете мониторинг сертификатов в своей инфраструктуре?

Серверы для мониторинга

Закажите сервер с предустановленным ПО для мониторинга: Grafana, Zabbix, Prometheus и другими.

Узнать больше


ссылка на оригинал статьи https://habr.com/ru/articles/945776/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *