Перейти к содержимому

Уведомления об истечении регистрации домена

Речь не про 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 -eu
set -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
fi
done

Сделайте исполняемым:

Окно терминала
sudo chmod +x /usr/local/bin/notifly-domain-check

Нужен пакет whois (обычно apt install whois / dnf install whois). Он же понимает кириллические домены — TCINET корректно отдаёт paid-till и для .рф.

Домен обновляется раз в год, так что проверять чаще раза в сутки смысла нет.

Раз в день в 9 утра:

0 9 * * * root /usr/local/bin/notifly-domain-check \
example.com \
notifly.ru \
notifly.рф

Домены передаются аргументами — сколько угодно, разных зон.

/etc/systemd/system/notifly-domain.service:

[Unit]
Description=Notifly domain expiry check
[Service]
Type=oneshot
ExecStart=/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:00
Persistent=true
[Install]
WantedBy=timers.target

Persistent=true докладывает проверку, если сервер был выключен в 9 утра. Активируем:

Окно терминала
sudo systemctl daemon-reload
sudo systemctl enable --now notifly-domain.timer
sudo systemctl list-timers notifly-domain.timer

В 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.

C:\scripts\Notifly-Domain-Check.ps1
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 Highest
Register-ScheduledTask -TaskName "Notifly Domain Check" `
-Action $Action -Trigger $Trigger -Principal $Princ -Description "Notifly: проверка срока регистрации доменов"
  • Домен не «отвалится» внезапно. За месяц приходит спокойное предупреждение, за неделю — громкое: забыть уже невозможно.
  • Работает с любыми зонами, включая .ru и .рф (поле paid-till от TCINET) — один скрипт на весь парк доменов, зоны передаются аргументами.
  • Отдельная защита от той самой ошибки, когда SSL продлевается автоматически, а про оплату самого домена все забыли, потому что «сайт же работает».
  • Прикладывать в сообщение имя регистратора и статус (clientTransferProhibited и т. п.) через поле extras — сразу видно, куда идти продлевать.
  • Хранить состояние «уже предупредили», как в рецепте про диск, чтобы не слать одно и то же уведомление каждый день.
  • Проверять заодно и срок делегирования у регистратора NS-серверов — иногда «протухает» именно он, а не сам домен.