Автообновление — хорошо. Но что если certbot не сможет обновить сертификат? DNS лёг, API Cloudflare вернул ошибку, закончилось место на диске — вариантов масса. Нужен независимый мониторинг, который предупредит заранее, а не после того как всё упало.
Написали скрипт с двойной проверкой: снаружи — через реальное TLS-подключение, и изнутри — по файлам на диске:
#!/bin/bash
# /opt/scripts/check-ssl-expiry.sh
# Проверяет SSL-сертификаты всех доменов и алертит при приближении срока
DOMAINS=(
"mebel-store.ru"
"stroydom-kazan.ru"
"kazan-flowers.ru"
"auto-parts-kzn.ru"
"beauty-salon-kazan.ru"
"restoran-kazanochka.ru"
# ... все 45 доменов
)
WARN_DAYS=21 # Предупреждение за 21 день
CRIT_DAYS=7 # Критический алерт за 7 дней
BOT_TOKEN="YOUR_BOT_TOKEN"
CHAT_ID="YOUR_CHAT_ID"
LOGFILE="/var/log/ssl-check.log"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] SSL check started" >> "$LOGFILE"
for domain in "${DOMAINS[@]}"; do
# Получаем дату истечения через TLS-подключение
EXPIRY=$(echo | openssl s_client -servername "$domain" -connect "$domain":443 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "$EXPIRY" ]; then
MSG="SSL CHECK FAILED: $domain — не удалось подключиться"
echo "[$(date)] $MSG" >> "$LOGFILE"
curl -s "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d chat_id="${CHAT_ID}" -d text="$MSG" > /dev/null
continue
fi
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt 0 ]; then
MSG="SSL EXPIRED: $domain истёк $((DAYS_LEFT * -1)) дней назад!"
curl -s "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d chat_id="${CHAT_ID}" -d text="$MSG" > /dev/null
elif [ $DAYS_LEFT -lt $CRIT_DAYS ]; then
MSG="SSL CRITICAL: $domain истекает через $DAYS_LEFT дней ($EXPIRY)"
curl -s "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d chat_id="${CHAT_ID}" -d text="$MSG" > /dev/null
elif [ $DAYS_LEFT -lt $WARN_DAYS ]; then
MSG="SSL WARNING: $domain истекает через $DAYS_LEFT дней ($EXPIRY)"
curl -s "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d chat_id="${CHAT_ID}" -d text="$MSG" > /dev/null
fi
echo "[$(date)] $domain: $DAYS_LEFT дней" >> "$LOGFILE"
done
echo "[$(date '+%Y-%m-%d %H:%M:%S')] SSL check completed" >> "$LOGFILE"
# Добавляем в cron (ежедневная проверка в 9:00)
echo '0 9 * * * root /opt/scripts/check-ssl-expiry.sh' | sudo tee /etc/cron.d/ssl-check
sudo chmod 644 /etc/cron.d/ssl-check
Для полноценного мониторинга развернули blackbox_exporter — компонент Prometheus, который проверяет внешние эндпоинты и умеет снимать SSL-метрики:
# Установка blackbox_exporter
wget https://github.com/prometheus/blackbox_exporter/releases/download/v0.25.0/blackbox_exporter-0.25.0.linux-amd64.tar.gz
tar xvf blackbox_exporter-0.25.0.linux-amd64.tar.gz
sudo mv blackbox_exporter-0.25.0.linux-amd64/blackbox_exporter /usr/local/bin/
# Конфигурация blackbox_exporter
sudo tee /etc/blackbox_exporter/config.yml > /dev/null <<'EOF'
modules:
http_2xx_ssl:
prober: http
timeout: 10s
http:
valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
valid_status_codes: [200, 301, 302]
method: GET
tls_config:
insecure_skip_verify: false
fail_if_ssl: false
fail_if_not_ssl: true
EOF
# Systemd unit
sudo tee /etc/systemd/system/blackbox-exporter.service > /dev/null <<'EOF'
[Unit]
Description=Blackbox Exporter
After=network.target
[Service]
ExecStart=/usr/local/bin/blackbox_exporter --config.file=/etc/blackbox_exporter/config.yml
Restart=always
User=nobody
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now blackbox-exporter
Конфигурация Prometheus для сбора SSL-метрик:
# Добавляем в /etc/prometheus/prometheus.yml
- job_name: 'ssl-monitoring'
metrics_path: /probe
params:
module: [http_2xx_ssl]
static_configs:
- targets:
- https://mebel-store.ru
- https://stroydom-kazan.ru
- https://kazan-flowers.ru
- https://auto-parts-kzn.ru
- https://beauty-salon-kazan.ru
# ... все 45 доменов
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: localhost:9115 # blackbox_exporter
# Алерты для SSL в Prometheus
# /etc/prometheus/rules/ssl_alerts.yml
groups:
- name: ssl
rules:
- alert: SSLCertExpiringSoon
expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 21
for: 1h
labels:
severity: warning
annotations:
summary: "SSL сертификат {{ $labels.instance }} истекает менее чем через 21 день"
description: "Осталось {{ $value | humanizeDuration }}"
- alert: SSLCertExpireCritical
expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 7
for: 10m
labels:
severity: critical
annotations:
summary: "SSL сертификат {{ $labels.instance }} истекает менее чем через 7 дней!"
- alert: SSLCertExpired
expr: probe_ssl_earliest_cert_expiry - time() < 0
for: 1m
labels:
severity: critical
annotations:
summary: "SSL сертификат {{ $labels.instance }} ИСТЁК!"
- alert: SSLProbeFailure
expr: probe_success{job="ssl-monitoring"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Не удалось подключиться к {{ $labels.instance }} по HTTPS"