Уведомления об истечении регистрации домена
Речь не про SSL-сертификат: тот можно перевыпустить за минуту, а вот регистрацию самого доменного имени так просто не вернуть. Если забыть продлить домен, он уходит в redemption period, потом на аукцион — и сайт вместе с почтой перестаёт существовать. Certbot тут не спасёт: сертификат может быть свежий, а домен — уже чужой.
Проблема в том, что дату окончания регистрации отдаёт whois, а каждый регистратор
называет поле по-своему: Registry Expiry Date, Expiration Date, Expiry date,
paid-till (так делают .ru/.рф через TCINET) и десяток других вариантов. Форматы
дат тоже разные — от ISO до YYYY.MM.DD. Ниже — скрипт, который переваривает
все распространённые варианты, нормализует дату через date -d и шлёт
пуш, когда до окончания остаётся мало дней.
Скрипт проверки
Заголовок раздела «Скрипт проверки»/usr/local/bin/notifly-domain-check:
#!/usr/bin/env bash# Использование:# notifly-domain-check example.com example.ru notifly.рф [...]set -euset -a; source /etc/notifly.env; set +a
WARN_DAYS=30 # приоритет 5 (обычное предупреждение)CRIT_DAYS=7 # приоритет 9 (громкое, heads-up)
# Достаёт «сырую» дату окончания регистрации из вывода whois.# Перебираем ярлыки разных регистраторов сверху вниз, берём первое совпадение.extract_expiry() { local w="$1" # .ru / .рф / .su через TCINET: поле paid-till, формат YYYY.MM.DD local ru ru=$(echo "$w" | grep -iE '^\s*paid-till:' | head -n1 | sed -E 's/^[^:]*:\s*//') if [ -n "$ru" ]; then # 2027.04.15 -> 2027-04-15, чтобы date -d понял наверняка echo "${ru//./-}" return fi # Все остальные TLD: типовые ярлыки для даты окончания echo "$w" | grep -iE \ 'Registry Expiry Date|Registrar Registration Expiration Date|Expiration Date|Expiration Time|Expiry Date|Expiry date|expire:|expires:|Domain Expiration Date|renewal date' \ | head -n1 | sed -E 's/^[^:]*:\s*//'}
for DOMAIN in "$@"; do WHOIS=$(whois "$DOMAIN" 2>/dev/null || true)
if [ -z "$WHOIS" ]; then /usr/local/bin/notifly-send \ "🚨 Домен: whois не ответил — $DOMAIN" \ "Не удалось получить данные whois. Проверьте сеть или лимит запросов." 8 continue fi
RAW=$(extract_expiry "$WHOIS")
if [ -z "$RAW" ]; then /usr/local/bin/notifly-send \ "🚨 Домен: не нашёл дату — $DOMAIN" \ "В выводе whois нет знакомого поля с датой. Проверьте вручную: whois $DOMAIN" 8 continue fi
# Нормализуем в unix-время. Если формат неожиданный — сообщаем, а не молчим. if ! EXP_TS=$(date -d "$RAW" +%s 2>/dev/null); then /usr/local/bin/notifly-send \ "🚨 Домен: не смог разобрать дату — $DOMAIN" \ "whois вернул: $RAW. Формат не распознан, проверьте вручную." 8 continue fi
NOW_TS=$(date +%s) DAYS_LEFT=$(( (EXP_TS - NOW_TS) / 86400 )) EXP_HUMAN=$(date -d "@$EXP_TS" +%Y-%m-%d)
if [ "$DAYS_LEFT" -le "$CRIT_DAYS" ]; then /usr/local/bin/notifly-send \ "🔥 Домен КРИТИЧНО: $DOMAIN — $DAYS_LEFT дн." \ "Регистрация истекает $EXP_HUMAN. Срочно продлите, иначе потеряете домен!" 9 elif [ "$DAYS_LEFT" -le "$WARN_DAYS" ]; then /usr/local/bin/notifly-send \ "⚠️ Домен: $DOMAIN истекает через $DAYS_LEFT дн." \ "Регистрация домена действует до $EXP_HUMAN. Пора продлевать." 5 fidoneСделайте исполняемым:
sudo chmod +x /usr/local/bin/notifly-domain-checkНужен пакет whois (обычно apt install whois / dnf install whois). Он же
понимает кириллические домены — TCINET корректно отдаёт paid-till и для .рф.
Домен обновляется раз в год, так что проверять чаще раза в сутки смысла нет.
Через cron
Заголовок раздела «Через cron»Раз в день в 9 утра:
0 9 * * * root /usr/local/bin/notifly-domain-check \ example.com \ notifly.ru \ notifly.рфДомены передаются аргументами — сколько угодно, разных зон.
Через systemd timer (рекомендуется)
Заголовок раздела «Через systemd timer (рекомендуется)»/etc/systemd/system/notifly-domain.service:
[Unit]Description=Notifly domain expiry check
[Service]Type=oneshotExecStart=/usr/local/bin/notifly-domain-check example.com notifly.ru notifly.рф/etc/systemd/system/notifly-domain.timer:
[Unit]Description=Run notifly-domain-check daily
[Timer]OnCalendar=*-*-* 09:00:00Persistent=true
[Install]WantedBy=timers.targetPersistent=true докладывает проверку, если сервер был выключен в 9 утра.
Активируем:
sudo systemctl daemon-reloadsudo systemctl enable --now notifly-domain.timersudo systemctl list-timers notifly-domain.timerWindows: PowerShell + Task Scheduler
Заголовок раздела «Windows: PowerShell + Task Scheduler»В Windows нет встроенного whois, поэтому берём whois.exe из Sysinternals
(https://learn.microsoft.com/sysinternals/downloads/whois) — распакуйте его,
например, в C:\Tools\whois.exe. При первом запуске он попросит принять
лицензию: whois.exe -accepteula example.com.
Скрипт использует общую функцию Send-Notifly из
шаблона sysadmin/index.
param( [string[]]$Domains = @("example.com", "notifly.ru"), [string] $WhoisExe = "C:\Tools\whois.exe"). C:\scripts\Notifly.ps1
$WarnDays = 30$CritDays = 7
# Ярлыки полей с датой окончания у разных регистраторов$patterns = @( 'Registry Expiry Date', 'Registrar Registration Expiration Date', 'Expiration Date', 'Expiry Date', 'Expiry date', 'Domain Expiration Date', 'expire', 'paid-till')
foreach ($domain in $Domains) { try { $whois = & $WhoisExe -nobanner $domain 2>$null
$raw = $null foreach ($p in $patterns) { $line = $whois | Select-String -Pattern ([regex]::Escape($p)) | Select-Object -First 1 if ($line) { $raw = ($line.Line -split ':', 2)[1].Trim() # .ru/.рф paid-till: YYYY.MM.DD -> YYYY-MM-DD if ($p -eq 'paid-till') { $raw = $raw -replace '\.', '-' } break } }
if (-not $raw) { Send-Notifly -Title "🚨 Домен: не нашёл дату — $domain" ` -Message "В выводе whois нет знакомого поля. Проверьте вручную." -Priority 8 continue }
$exp = [datetime]::Parse($raw, [System.Globalization.CultureInfo]::InvariantCulture) $daysLeft = [int]($exp - (Get-Date)).TotalDays $expHuman = $exp.ToString('yyyy-MM-dd')
if ($daysLeft -le $CritDays) { Send-Notifly -Title "🔥 Домен КРИТИЧНО: $domain — $daysLeft дн." ` -Message "Регистрация истекает $expHuman. Срочно продлите!" -Priority 9 } elseif ($daysLeft -le $WarnDays) { Send-Notifly -Title "⚠️ Домен: $domain истекает через $daysLeft дн." ` -Message "Регистрация действует до $expHuman. Пора продлевать." -Priority 5 } } catch { Send-Notifly -Title "🚨 Домен: ошибка проверки $domain" ` -Message "$_" -Priority 8 }}Если ставить сторонний whois.exe нельзя, дату окончания можно посмотреть у RDAP-сервиса
регистратуры (Invoke-RestMethod "https://rdap.org/domain/$domain"), но полнота
данных зависит от зоны — для .ru RDAP часто отдаёт меньше, чем whois.
Расписание — раз в сутки:
$Action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\scripts\Notifly-Domain-Check.ps1"$Trigger = New-ScheduledTaskTrigger -Daily -At 09:00$Princ = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel HighestRegister-ScheduledTask -TaskName "Notifly Domain Check" ` -Action $Action -Trigger $Trigger -Principal $Princ -Description "Notifly: проверка срока регистрации доменов"- Домен не «отвалится» внезапно. За месяц приходит спокойное предупреждение, за неделю — громкое: забыть уже невозможно.
- Работает с любыми зонами, включая
.ruи.рф(полеpaid-tillот TCINET) — один скрипт на весь парк доменов, зоны передаются аргументами. - Отдельная защита от той самой ошибки, когда SSL продлевается автоматически, а про оплату самого домена все забыли, потому что «сайт же работает».
Что улучшить дальше
Заголовок раздела «Что улучшить дальше»- Прикладывать в сообщение имя регистратора и статус (
clientTransferProhibitedи т. п.) через полеextras— сразу видно, куда идти продлевать. - Хранить состояние «уже предупредили», как в рецепте про диск, чтобы не слать одно и то же уведомление каждый день.
- Проверять заодно и срок делегирования у регистратора NS-серверов — иногда «протухает» именно он, а не сам домен.