PunchKano begins diphtheria immunisation in five Kano LGsRTP DesportoFrancisco Cabral na final de pares em HangzhouESPN DeportesGrecia derrotó a Alemania por primera vez en la historia y golpeó a Klopp en su debut ante su públicoThe Jerusalem PostIsrair awaits final approval for Tokyo, Miami flights, adds new European destinations for 2027Daily MaverickWe worried AI would make things up, we should also worry when it doesn’tInquirerRains to continue in Visayas, Mindanao due to ITCZ until Sept. 30Bollywood HungamaVinod Kapri's Pyre to release in theaters on October 23, 2026BlickNächstes Amt abgelegt: Jens Spahn zieht sich aus Haushaltsausschuss zurückRapplerPhilippines should fix tax gaps and procurement, not raise tax rates – WBSportstarIndia in Athletics LIVE Updates, Asian Games 2026: Vithya Ramraj breaks National Record to win 400m hurdles bronze; Javelin throw final at 4:45 PM ISTIl Fatto QuotidianoMorto Stefano Milani, il tifoso del Milan diventato famoso su X. Da Bertolucci a Valenti: “Ha lottato come nessuno, mai un passo indietro”7sur7“Pourquoi je perds toujours?”: quand André Agassi taquine Alexander Zverev
The Daily Newsstand · Free, Always
Monday, September 28, 2026

Мониторинг SSL сертификатов в Zabbix

Translate

Когда ты имеешь несколько десятков сертификатов под своей ответственностью — это нормально. Часть из них будет продлеваться автоматически, а которые необходимо продлевать вручную, ты занесешь в календарь и поставишь напоминания. Пару раз в год на телефоне или рабочем ноутбуке будет выскакивать уведомление. Но если под твоей ответственностью тысячи сертификатов, да еще и на разных серверах и в разных филиалах — это чуть‑чуть усложняет задачу.

Окей, возможно, ты гений автоматизации и у тебя все продлевается автоматически, а ты просто попиваешь кофе и наблюдаешь за тем, как работа работается сама. Но всегда что‑то может пойти не так, и мы должны быть уверены, что в этом случае в нашем любимом Zabbix загорится триггер бордового цвета, который будет мозолить глаза на всех мониторах и долетит до всех мессенджеров.

Если, конечно, ты не хочешь, чтобы тебя будили в 2 часа ночи, потому что коллега в другом городе — где рабочий день уже начался — не может подключиться к корпоративной сети по VPN. Потому что сертификат истёк.

Не так давно наши системные администраторы собрали сертификаты со всех CA в единую базу — Netbox. Им удобно: все в одном месте. Нам не менее удобно: не нужно лезть на каждый CA, чтобы вытянуть данные по сертификатам. Всего один API запрос, и все сертификаты на нашем zabbix proxy. Красотень.

Итак, была поставлена задача мониторить следующие ключи:

  • sans — список всех дополнительных доменных имён или IP, для которых действителен сертификат. Часто может быть пустым.

  • days_remaining — то, ради чего все это затевается.

  • valid_to — фактическая дата истечения срока действия.

  • fingerprint_sha256 — уникальный хеш сертификата.

  • issuer — кто подписал сертификат. В нашем случае он полезен тем, что показывает, на какой CA лезть. Если у вас только один CA, то это значение бесполезно, вы и так знаете, куда вам идти.

Отдельно хочу отметить:

  • common_name — мы не будем его хранить, а будем подставлять вместо sans, когда он пустой.

  • id — именно по нему будет работать правило обнаружения: каждый сертификат становится отдельным набором элементов данных в Zabbix.

Чтобы изучить, какие ключи есть у каждого сертификата, вы можете вытянуть один сертификат с помощью curl и изучить все ключи и их значения:

TOKEN=$(< /etc/zabbix/scripts/netbox.token)

curl -s -X GET \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Accept: application/json" \
  "https://example.com/api/plugins/ssl/certificates/?limit=1" \
| jq '.results[0]'

Теперь давайте разберем структуру проекта на zabbix proxy:

/etc/zabbix/scripts/
├── certificates.txt
├── get_certificates.sh
├── netbox.token
├── ssl_sender.sh
  • certificates.txt — сюда будем складывать все сертификаты.

  • get_certificates.sh — скрипт, который тянет серты с нетбокса.

  • netbox.token — файл с АПИ токеном и правами 600.

  • ssl_sender.sh — этот скрипт отправит все данные в заббикс.

Перетягиваем данные из Netbox в Zabbix

Начнем с get_certificates.sh. Именно он заберет все сертификаты с нетбокса и аккуратненько сложит их в файлик.

#!/bin/bash
set -euo pipefail

TOKEN_FILE="/etc/zabbix/scripts/netbox.token"
[ -r "$TOKEN_FILE" ] || { echo "ERROR: $TOKEN_FILE not readable" >&2; exit 1; }
TOKEN=$(< "$TOKEN_FILE")

BASE_URL="https://example.com"
OUTPUT_FILE="/etc/zabbix/scripts/certificates.txt"
TMP_FILE="${OUTPUT_FILE}.tmp"

: > "$TMP_FILE"
chmod 600 "$TMP_FILE"

next_url="${BASE_URL}/api/plugins/ssl/certificates/?limit=100"

while [ -n "${next_url}" ] && [ "${next_url}" != "null" ]; do
    response=$(curl -sf -X GET \
        -H "Authorization: Bearer ${TOKEN}" \
        -H "Content-Type: application/json" \
        "${next_url}") || { echo "ERROR: curl failed for $next_url" >&2; exit 1; }

    echo "$response" | jq -c '.results[] |
        select(.days_remaining >= 0) |
        {
          id: (.id | tostring),
          sans: (if (.sans | length) > 0 then (.sans | join(" ")) else (.common_name // "") end),
          days_remaining: .days_remaining,
          valid_to: (.valid_to | split("T")[0] + " " + (split("T")[1] | .[0:5])),
          fingerprint_sha256: .fingerprint_sha256,
          issuer: (.issuer // "")
        }' \
        >> "$TMP_FILE" || { echo "ERROR: jq failed" >&2; exit 1; }

    next_url=$(echo "$response" | jq -r '.next')
done

mv "$TMP_FILE" "$OUTPUT_FILE"

Скрипт выполняет запрос в нетбокс, вытягивает оттуда все сертификаты с необходимыми ключами и кладет их в файл certificates.txt. Вот как выглядит один сертификат:

{“id”:“999”,“sans”:“my-test-server 10.20.30.40”,“days_remaining”:777,“valid_to”:“2030-01-15 08:22”,“fingerprint_sha256”:“11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00”,“issuer”:“CN=Test Root CA, O=Fake Company, C=XX”}

Когда вся база сертов у нас в руках, нужно все это дело как‑то отправить в заббикс. Хочу сказать, что изначально мы тупанули и создали скрипт, который подразумевал, что заббикс прокси будет сам вытягивать из файла все сертификаты через правило обнаружения и потом лезть за каждым значением. В нашей компании более 7000 сертификатов, умножьте их на 5 ключей, и вы получите более 35 000 элементов данных. Прокси начал задыхаться, а очередь из этих 35 000 значений выстроилась и не собиралась рассасываться.

Так мы пришли к оптимальному решению: zabbix trapper. Это просто чудо чудесное. Одна команда, одна секунда, и данные в вашем заббикс. Никакой нагрузки на прокси и никаких очередей.

А вот скрипт ssl_sender.sh, который выполняет всю работу по отправке:

#!/bin/bash
set -euo pipefail

CERT_FILE="/etc/zabbix/scripts/certificates.txt"
ZABBIX_SERVER="127.0.0.1"
ZABBIX_PORT="10051"
ZABBIX_HOST="SSL Monitoring"

[ -r "$CERT_FILE" ] || { echo "ERROR: $CERT_FILE not readable" >&2; exit 1; }

TMPDIR=$(mktemp -d /tmp/ssl_sender.XXXXXX)
trap 'rm -rf "$TMPDIR"' EXIT

DISCOVERY_FILE="$TMPDIR/discovery.txt"
METRICS_FILE="$TMPDIR/metrics.txt"

jq -s -c --arg h "$ZABBIX_HOST" '
  { data: [ .[] | { "{#ID}": .id, "{#SANS}": (.sans[0:150]) } ] }
' "$CERT_FILE" \
  | jq -r --arg h "$ZABBIX_HOST" '"\"\($h)\" ssl.cert.discovery \(@json)"' \
  > "$DISCOVERY_FILE"

jq -s -r --arg h "$ZABBIX_HOST" '
  .[] |
  "\"\($h)\" ssl.cert.days_left[\(.id)] \(.days_remaining)",
  "\"\($h)\" ssl.cert.valid_to[\(.id)] \(.valid_to | @json)",
  "\"\($h)\" ssl.cert.sans[\(.id)] \(.sans | @json)",
  "\"\($h)\" ssl.cert.fingerprint[\(.id)] \(.fingerprint_sha256 | @json)",
  "\"\($h)\" ssl.cert.issuer[\(.id)] \(.issuer | @json)"
' "$CERT_FILE" > "$METRICS_FILE"

zabbix_sender -z "$ZABBIX_SERVER" -p "$ZABBIX_PORT" -i "$DISCOVERY_FILE"
sleep 5
zabbix_sender -z "$ZABBIX_SERVER" -p "$ZABBIX_PORT" -i "$METRICS_FILE"

Обратите внимание на ZABBIX_HOST. Хост с таким именем должен быть создан в вашем заббикс, чтобы данные корректно отправились. Также на него необходимо накинуть заббикс шаблон, который мы создадим позже.

jq -s -c --arg h "$ZABBIX_HOST" '
  { data: [ .[] | { "{#ID}": .id, "{#SANS}": (.sans[0:150]) } ] }
' "$CERT_FILE" \
  | jq -r --arg h "$ZABBIX_HOST" '"\"\($h)\" ssl.cert.discovery \(@json)"' \
  > "$DISCOVERY_FILE"

Здесь мы указываем, по какому ключу у нас будет работать правило обнаружения. В нашем случае по ключу id. Также указываем имя ключа для правила обнаружения в нашем будущем шаблоне. Оно может быть любым. Ну, почти любым. Старайтесь соблюдать стандарты заббикс: разделяйте слова в ключе точками, как в нашем примере ssl.cert.discovery.

  "\"\($h)\" ssl.cert.days_left[\(.id)] \(.days_remaining)",
  "\"\($h)\" ssl.cert.valid_to[\(.id)] \(.valid_to | @json)",
  "\"\($h)\" ssl.cert.sans[\(.id)] \(.sans | @json)",
  "\"\($h)\" ssl.cert.fingerprint[\(.id)] \(.fingerprint_sha256 | @json)",
  "\"\($h)\" ssl.cert.issuer[\(.id)] \(.issuer | @json)"

А это прототипы элементов данных. ssl.cert.days_left — имя ключа в шаблоне заббикс. Оно также может быть любым, но должно совпадать в скрипте и в шаблоне. days_remaining — ключ в файле certificates.txt. Из этого ключа заббикс будет брать значение.

zabbix_sender -z "$ZABBIX_SERVER" -p "$ZABBIX_PORT" -i "$DISCOVERY_FILE"
sleep 5
zabbix_sender -z "$ZABBIX_SERVER" -p "$ZABBIX_PORT" -i "$METRICS_FILE"

А здесь происходит отправка данных на сервер. Сначала срабатывает правило обнаружения: создаются все элементы данных. И только потом отправляются все значения. Между ними стоит sleep 5, но могу сказать, что это бесполезно, так как 5 секунд — это очень мало. Сами элементы данных то создадутся, но заббикс прокси не успеет получить о них информацию от сервера. Чтобы это правило работало корректно, нужно ставить слип на 3–5 минут. Но мы оставили так, как есть. Просто за первый проход скрипт отрабатывает дискавери, а за второй уже отправляет метрики. Нас это устраивает.

Создание шаблона

В первую очередь создаем новый шаблон. Мы его назвали SSL Certificates Monitoring trapper. И внутри шаблона создаем правило обнаружения.

Еще раз обратите внимание, что имя ключа точно такое же, как в нашем скрипте ssl_sender.sh.

Еще раз обратите внимание, что имя ключа точно такое же, как в нашем скрипте ssl_sender.sh.

После создания правила обнаружения создаем для него прототипы элементов данных.

Прототипы элементов данных

Прототипы элементов данных

В имя элементов данных мы вставили только ID, поскольку все остальное у нас будет отображаться в значениях. Сначала в имени у нас также содержался SANs, но впоследствии мы посчитали, что это лишнее. Имейте ввиду, что у имени элемента данных есть ограничение 250 символов. Если полное имя вместе с SANs и ID будет превышать эту длину, то элемент данных просто не создастся.

Теперь осталось создать триггеры.

Триггеры

Триггеры

Как вы можете заметить, мы сделали три триггера:

  • Средняя важность, если сертификату осталось жить меньше 28 дней

  • Высокая важность, если сертификату осталось жить меньше 14 дней

  • Чрезвычайная важность, если сертификату осталось жить меньше 2 дней

Осталось создать хост и накинуть на него данный шаблон.

Здесь самое важное, чтобы имя узла совпадало с тем, которое прописано у нас в скрипте ssl_sender.sh. В нашем случае это SSL Monitoring. Также правильно укажите адрес агента.

Отправка данных в Zabbix

Вся основная часть готова, осталось запустить скрипт ssl_sender.sh и дождаться результата. Не забудьте дать всем скриптам права на исполнение. После запуска скрипта, вы должны увидеть следующее:

zabbix@zabbixproxy:/etc/zabbix/scripts# ./ssl_sender.sh
Response from "127.0.0.1:10051": "processed: 1; failed: 0; total: 1; seconds spent: 0.008893"
sent: 1; skipped: 0; total: 1
Response from "127.0.0.1:10051": "processed: 250; failed: 0; total: 250; seconds spent: 0.002481"
sent: 250; skipped: 0; total: 250

Сначала срабатывает правило обнаружения. Потом срабатывает отправка метрик. Все успешно. 1 правило, 250 метрик. У меня этот скрипт уже отрабатывал, поэтому все прошло без ошибок. Если вы будете запускать впервые, то скорее всего правило обнаружения отработает успешно, а метрики упадут в ошибку. Об этом я писал выше. Просто запустите скрипт повторно через 3–5 минут.

Так элементы данных выглядят в заббикс:

SANs и Issuer замазал

SANs и Issuer замазал

Осталось создать расписание запуска скрипта. Я лично почти всегда создаю отдельный юнит, но данный процесс мы уже разбирать не будем. Всем удачи и поменьше красных триггеров.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.