На сервере openvpn установлен и настроен в режиме релея почтовик postfix, переправляющий по SMTP от адреса vpn@domain.ru письма с уведомлениями на сервер mail.ru.
В каталоге /root/CA живёт скрипт cert_notification.sh, запускаемый из crontab пользователя root каждый день в 9 утра.
Скрипт ищет в файле /root/CA/sbmCA/index.txt сертификаты, срок которых истечёт через 14 дней, отправляет письмо на адрес _CN_@domain.ru и копию на адрес admin@domain.ru.
В файле /var/lib/openvpn/cache/notified_certs.txt скрипт отмечает сертификаты, по которым уже было отправлено уведомление, чтобы не повторять отправку.
В файле /var/log/openvpn_cert_notify.log пишется лог скрипта.
Скрипт:
#!/bin/bash
# ==================== НАСТРОЙКИ ====================
# Путь к файлу index.txt (обычно /etc/openvpn/easy-rsa/pki/index.txt)
INDEX_FILE="/Путь к файлу с индексом сертификатов/index.txt"
# Домен, добавляемый к CN для формирования email (можно сменить на свой)
EMAIL_DOMAIN="domain.ru"
ADMIN_EMAIL="admin@domain.ru"
# Файл для учёта уже отправленных уведомлений (чтобы не слать повторно)
NOTIFY_FILE="/var/lib/openvpn/cache/notified_certs.txt"
# Лог-файл оповещений
LOG_FILE="/var/log/openvpn_cert_notify.log"
# Команда отправки письма (mail или mailx, должна быть установлена)
MAIL_CMD="mail"
echo "Скрипт начинает работу." >> "$LOG_FILE"
# ==================== ПРОВЕРКИ ====================
mkdir -p "$(dirname "$NOTIFY_FILE")" "$(dirname "$LOG_FILE")"
touch "$NOTIFY_FILE" "$LOG_FILE"
if [ ! -f "$INDEX_FILE" ]; then
echo "Ошибка: файл $INDEX_FILE не найден." >&2
exit 1
fi
# Получаем текущее время в секундах и дату +14 дней
NOW_EPOCH=$(date +%s)
TWO_WEEKS_EPOCH=$(date -d "+14 days" +%s)
# ==================== ОБРАБОТКА ====================
while IFS= read -r line; do
# Учитываем только действующие сертификаты (флаг V)
[[ "$line" == V* ]] || continue
# Разбираем поля. Формат: статус дата ревокейшн серийный имя_файла subject
read -r status expiry_date serial unknown subject <<< "$line"
# Проверяем наличие даты
if [ -z "$expiry_date" ]; then
echo "Пропущена строка без даты истечения: $line" >> "$LOG_FILE"
continue
fi
# Преобразуем дату формата YYMMDDHHMMSSZ (UTC) в секунды
yy=${expiry_date:0:2}; mm=${expiry_date:2:2}; dd=${expiry_date:4:2}
hh=${expiry_date:6:2}; MM=${expiry_date:8:2}; ss=${expiry_date:10:2}
year="20${yy}" # для 21xx встретится вряд ли, иначе нужна коррекция
# Пробуем получить epoch
expiry_epoch=$(date -d "${year}-${mm}-${dd} ${hh}:${MM}:${ss} UTC" +%s 2>/dev/null)
if [ -z "$expiry_epoch" ]; then
echo "Невозможно преобразовать дату: $expiry_date" >> "$LOG_FILE"
continue
fi
# Интересуют сертификаты, которые истекают в ближайшие 14 дней, но ещё не истекли
if [ "$expiry_epoch" -le "$NOW_EPOCH" ] || [ "$expiry_epoch" -gt "$TWO_WEEKS_EPOCH" ]; then
continue
fi
# Извлекаем CN из subject (поле /CN=...)
cn=$(echo "$subject" | sed -n 's/.*\/CN=\([^\/]*\).*/\1/p')
if [ -z "$cn" ]; then
echo "Не удалось извлечь CN из: $line" >> "$LOG_FILE"
continue
fi
# Проверяем, отправляли ли мы уже уведомление для этого CN с такой датой
if grep -q "^${cn}:${expiry_date}$" "$NOTIFY_FILE"; then
continue
fi
# Формируем email
email="${cn}@${EMAIL_DOMAIN}"
# Тема и текст письма (можно дополнить)
subject_msg="Срок действия конфига OpenVPN истекает через две недели"
body_msg="Здравствуйте, ${cn}.
Ваш конфиг для подключения к OpenVPN (серийный номер: ${serial}) истекает ${year}-${mm}-${dd}.
Если планируете далее использовать openVPN, запросите новый конфиг в отделе ИТ.
С уважением,
команда отдела ИТ"
# Отправка
echo "$body_msg" | $MAIL_CMD -s "$subject_msg" "$email" 2>>"$LOG_FILE"
if [ $? -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') Отправлено уведомление для CN=${cn} (email=${email})" >> "$LOG_FILE"
echo "${cn}:${expiry_date}" >> "$NOTIFY_FILE"
# --- Отправка копии администратору ---
admin_subject="Копия: уведомление об истечении сертификата пользователя ${cn}"
admin_body="Уважаемый администратор.
Пользователю ${cn} (${email}) отправлено уведомление об истечении сертификата OpenVPN.
Серийный номер: ${serial}
Дата истечения: ${year}-${mm}-${dd}
Текст письма пользователю:
---
${body_msg}
---
"
echo "$admin_body" | $MAIL_CMD -s "$admin_subject" "$ADMIN_EMAIL" 2>>"$LOG_FILE"
if [ $? -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') Уведомление администратору отправлено (${ADMIN_EMAIL}) для CN=${cn}" >> "$LOG_FILE"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') Ошибка отправки уведомления администратору для CN=${cn}" >> "$LOG_FILE"
fi
# -------------------------------------
else
echo "$(date '+%Y-%m-%d %H:%M:%S') Ошибка отправки для CN=${cn}" >> "$LOG_FILE"
fi
done < "$INDEX_FILE"