Казалось бы, что может быть сложного в мониторинге сертификатов? Выдан сертификат – следите за его сроком действия. Многие по старинке используют календари, напоминания, иногда даже физические заметки. Но ручные методы неидеальны, ведь люди ошибаются, забывают и теряют информацию.
И если не автоматизировать такие моменты, то однажды можете проснуться и узнать, что сервис недоступен из-за протухшего сертификата. Давайте разберёмся, что к чему.
Глубже в технику: Grafana, Prometheus и наши экспортеры
Сначала запустим Grafana и Prometheus - это наши главные инструменты для мониторинга. Затем поболтаем об экспортерах и подключим один из них, добавим крутую панельку с графиками. Чтобы всё это дело не пропустить, научим Alertmanager слать нам уведомления в Telegram.
1. Начнем с основ: Grafana + Prometheus и Docker Compose
Для тех, кто в теме и уже тысячу раз слышал о awesome-compose/prometheus-grafana, шаг можно пропустить. Для остальных - вам понадобится compose.yml файл:
services: prometheus: image: prom/prometheus container_name: prometheus command: - "--config.file=/etc/prometheus/prometheus_config.yml" ports: - 9090:9090 restart: unless-stopped volumes: - ./prometheus:/etc/prometheus - prom_data:/prometheus grafana: image: grafana/grafana container_name: grafana ports: - 3000:3000 restart: unless-stopped env_file: .env environment: - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} volumes: - ./grafana:/etc/grafana/provisioning/datasources volumes: prom_data:
Теперь создайте директорию prometheus с файлом prometheus_config.yml:
global: scrape_interval: 15s scrape_timeout: 10s evaluation_interval: 15s
И то же самое для директории grafana и файла datasource.yml:
apiVersion: 1 datasources: - name: Prometheus type: prometheus url: http://prometheus:9090 isDefault: true access: proxy editable: true
В примере будем хранить секреты в файле:
.env. Но лучше так не делать, потому что файл может легко попасть в открытый доступ. Используйте для хранения секретов специальные инструменты, шифруйте файлы и все будет хорошо.
Создайте файл .env и добавьте ИМЯ_ПОЛЬЗОВАТЕЛЯ_GRAFANA и ПАРОЛЬ_ПОЛЬЗОВАТЕЛЯ_GRAFANA:
GF_SECURITY_ADMIN_USER=<ИМЯ_ПОЛЬЗОВАТЕЛЯ_GRAFANA> GF_SECURITY_ADMIN_PASSWORD=<ПАРОЛЬ_ПОЛЬЗОВАТЕЛЯ_GRAFANA>
Поднимаем всю эту красоту:
docker-compose up -d
Т��к, магия Docker сработала, и у нас есть Grafana на порту 3000 и Prometheus на 9090. Просто? Просто!
2. Погружаемся в мир экспортеров: выбор инструмента
Каждый экспортер имеет свои плюсы и минусы. Ведь как говорится, каждая кисть хороша
на своем холсте. Разберемся, какой из них лучше всего подойдет для вас:
ssl_exporter: его задача — сертификаты и только. Дает богатую информацию о сертификатах: даты истечения, алгоритмы, подписи и многое другое. Поддерживает различные моды для проверки: TCP, HTTPS, SMTP и так далее. Но если нужно больше, чем просто мониторинг SSL/TLS, придется искать дополнительные инструменты.
blackbox_exporter: умеет делать многое, он все проверит, в том числе и сертификаты. Если сравнивать с ssl_exporter, детализация данных и возможности могут показаться слабее. Избыточен, если нужен только мониторинг сертификатов, но отлично подойдет для комплексного мониторинга.
node-cert-exporter: если вам нужно отслеживать сертификаты на диске, это ваш выбор. Очень прост в использовании и не требует больших знаний.
x509-certificate-exporter: дает подробную информацию о сертификатах. Гибкий, может извлекать данные файлов или секретов Kubernetes. Сложнее в настройке и эксплуатации по сравнению с некоторыми другими инструментами.
cert_exporter: поддерживает множество источников сертификатов, включая файловую систему, Kubernetes и другие. Весьма универсален для разных сценариев использования.
Итак, какой инструмент выбрать? Выбор инструмента – это искусство, и, как и в любом искусстве, здесь есть свои тонкости и нюансы. Выбирайте то, что соответствует вашим потребностям и не бойтесь экспериментировать.
3. Работа с ssl_exporter
Мы уже обсудили, что представляет собой ssl_exporter. Следит за сертификатами хорошо
и по-разному. Давайте добавим его в compose.yml:
ssl_exporter: image: ribbybibby/ssl-exporter ports: - "9219:9219"
Сегодня нашей целью будет prometheus.io. Добавляем scrape_configs в prometheus_config.yml:
scrape_configs: - job_name: "ssl-exporter" metrics_path: /probe static_configs: - targets: - prometheus.io:443 relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: ssl_exporter:9219
Так, перезапускаем наш compose.yml:
docker-compose restart
В Prometheus выполняем PromQL запрос – ssl_probe_success и если мы видим следующее, то значит настроили все верно:
ssl_probe_success{instance="prometheus.io:443", job="ssl-exporter"}
По умолчанию экспортер создает TCP-соединение с целью. В большинстве случаев этого хватает, но если вы хотите использовать проксирование через http, используйте модуль https. Кроме того, можно с помощью file следить за сертификатами на диске:
scrape_configs: - job_name: "ssl-files" metrics_path: /probe params: module: ["file"] target: ["/etc/ssl/cert.pem"] kubernetes_sd_configs: - role: node relabel_configs: - source_labels: [__address__] regex: ^(.*):(.*)$ target_label: __address__ replacement: ${1}:9219
Если сертификатов несколько, можно использовать глоб-поиск, что позволяет захватить сразу несколько файлов:
scrape_configs: - job_name: "ssl-files" metrics_path: /probe params: module: ["file"] target: ["/etc/ssl/**/*.pem"] kubernetes_sd_configs: - role: node relabel_configs: - source_labels: [__address__] regex: ^(.*):(.*)$ target_label: __address__ replacement: ${1}:9219
Также можно использовать в связке с Kubernetes, в документации больше информации.
4. Добавляем панель в Grafana
Велосипед изобретать не будем, а просто авторизовываемся и импортируем готовую панель из репозитория ssl_exporter:

Как не проспать проблемы
Что у нас в планах? Поднимем невероятно простой, но очень полезный сервис на Flask'е, который будет ловить уведомления от Alertmanager и пересылать их в Telegram. Создадим бот и настроим Alertmanager на отправку уведомлений.
1. Взлетаем с Flask
Ладно, не будем мелочиться, заводим директорию telegram и кидаем туда файлик app.py с элегантным, минималистичным и в стиле true pythonista кодом:
import os import requests from flask import Flask, request, jsonify app = Flask(__name__) TELEGRAM_TOKEN = os.environ["TELEGRAM_TOKEN"] CHAT_ID = os.environ["TELEGRAM_CHAT_ID"] TELEGRAM_URL = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage" @app.route("/alert", methods=["POST"]) def webhook(): data = request.json alertname = data["alerts"][0]["labels"]["alertname"] instance = data["alerts"][0]["labels"]["instance"] summary = data["alerts"][0]["annotations"]["summary"] text = f"⚠️ Alarm! {alertname} for {instance}. \n{summary}" payload = {"chat_id": CHAT_ID, "text": text} response = requests.post(TELEGRAM_URL, data=payload) return jsonify(status="success"), 200 if __name__ == "__main__": app.run(host="0.0.0.0", port=8000)
А еще пригодится Dockerfile:
FROM python:3.10-alpine WORKDIR /app COPY . /app RUN pip3 install -r requirements.txt ENTRYPOINT ["python3"] CMD ["app.py"]
Ну и, разумеется, незаменимый requirements.txt:
flask requests
2. Крафтим бота:
Запустим
@BotFatherв Telegram./newbot, даем ему имя и вуаля! У вас в руках:ТОКЕН_БОТА.Выясним свой chat_id, отправив
/startсвоему боту и заглянув наhttps://api.telegram.org/bot<ТОКЕН_БОТА>/getUpdates. Найдитеresult[0]message.chat.idи запомните, это будет вашВАШ_ЧАТ_ID.
Теперь добавьте всё это в compose.yml:
telegram: build: context: telegram stop_signal: SIGINT env_file: .env environment: - TELEGRAM_TOKEN=${TELEGRAM_TOKEN} - TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID} ports: - "8000:8000"
А секреты ТОКЕН_БОТА и ВАШ_ЧАТ_ID в .env :
TELEGRAM_TOKEN=<ТОКЕН_БОТА> TELEGRAM_CHAT_ID=<ВАШ_ЧАТ_ID>
3. Дружим Alertmanager с Telegram
Пришло время разобраться, куда именно будем слать уведомления. Теоретически Alertmanager может отправлять сообщения куда угодно, но мы-то знаем, что сегодня наш выбор – Telegram.
Начнем с малого. Напишем правило, которое даст нам знать, когда у сертификата останется менее 90 дней жизни. Для желающих углубиться - в документации собраны разные запросы. В prometheus создаем файл alert.rules.yml и пишем туда:
groups: - name: ssl-alerts rules: - alert: Expiry Soon expr: ssl_cert_not_after - time() < 86400 * 90 for: 1m labels: severity: critical annotations: summary: "SSL certificate is expiring soon (less than 90 days)"
Допишем пару строк в prometheus_config.yml:
rule_files: - "/etc/prometheus/alert.rules.yml" alerting: alertmanagers: - static_configs: - targets: - "alertmanager:9093"
Теперь давайте сделаем следующее: создадим директорию под названием alertmanager и положим в нее файл alertmanager_config.yml:
route: group_by: ["instance"] repeat_interval: 3h receiver: "telegram-webhook" receivers: - name: "telegram-webhook" webhook_configs: - url: "http://telegram:8000/alert"
Что ж, наш compose.yml тоже хочет внимания, добавляем контейнер alertmanager:
alertmanager: image: quay.io/prometheus/alertmanager container_name: alertmanager command: - "--config.file=/etc/alertmanager/alertmanager_config.yml" ports: - "9093:9093" volumes: - ./alertmanager:/etc/alertmanager
А теперь — магия:
docker-compose down; docker-compose up -d
Итак, смотрим, что наш Alertmanager чувствует себя комфортно:

и правило в Prometheus заняло свою рабочую позицию:

Примерно через минуту Prometheus поймет, что есть какие-то проблемы:

И еще примерно через 30 секунд Alertmanager отправит уведомление в наш сервис:

Который перешлет его к вам в Telegram:

Вот и всё! Теперь у вас в арсенале есть бот, который держит руку на пульсе ваших сервисов и, в случае чего, моментально бьет тревогу! Ну что, вы ведь тоже чувствуете этот аромат возможностей?
Заключение
Мониторинг сертификатов — это не мания, это забота. Наслаждайтесь комфортом, технологичностью, не забывайте поливать свои растения и мониторить сертификаты.
