Мониторинг SSL-сертификатов в oVirt Engine: как мы научились спать спокойно благодаря Go и Promethe…
- четверг, 11 сентября 2025 г. в 00:00:06
Авторы статьи: Артем Зубков, Junior администратор отдела DevOps.
В современных распределённых системах надёжность и безопасность инфраструктуры напрямую зависят от корректного функционирования криптографических компонентов, в частности — SSL/TLS-сертификатов. Одним из критически важных аспектов эксплуатации таких систем является своевременный мониторинг срока действия сертификатов, поскольку их просрочка может привести к нарушению работы сервисов, недоступности API, сбоям в аутентификации и даже компрометации безопасности соединений. В рамках экосистемы oVirt Engine, выступающего центральным узлом управления виртуальной инфраструктурой, особое значение имеют сертификаты, обеспечивающие защищённое взаимодействие между компонентами системы и конечными пользователями.
Серверы для мониторинга |
В данной статье мы рассмотрим практику реализации автоматизированного мониторинга срока действия двух ключевых сертификатов: 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
и получить вывод, похожий на тот, что изображен на скриншоте ниже:
Первым делом создаем алерт. Все примеры мы будем показывать ссылась на наш 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. Если проблем не возникло, то вывод будет примерно таким:
Если предыдущие шаги выполнены успешно, алерт будет выведен на общий дэшборд. Необходимо перенести его на дэшборд 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, мы получили гибкий и легко поддерживаемый компонент, который можно быстро адаптировать под другие типы сертификатов или системы. Важно, что теперь мониторинг стал проактивным — вместо реагирования на инциденты мы можем предотвращать их заранее.
А как вы организуете мониторинг сертификатов в своей инфраструктуре?
Серверы для мониторинга |