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

Уведомление о доступных обновлениях безопасности

Пакеты с известными уязвимостями, которые месяцами ждут apt upgrade, — это открытая дверь. Проблема не в том, что обновления сложно ставить, а в том, что о них легко забыть: никто не заходит на каждый сервер, чтобы посмотреть apt list --upgradable. Пусть сервер сам раз в неделю присылает дайджест — а если появились именно security-обновления, напоминает настойчивее.

Скрипт раз в неделю обновляет индекс пакетов и считает, сколько всего пакетов можно обновить и сколько из них — обновления безопасности:

  • если есть security-обновления — присылает дайджест с приоритетом 6–7 (нормальный, с уведомлением), чтобы вы не пропустили;
  • если есть только обычные обновления — тихий дайджест с приоритетом 3;
  • если обновлять нечего — по желанию присылает спокойное «всё актуально» с приоритетом 2 (можно отключить переменной NOTIFY_ALL_CLEAR).

Количество security-пакетов мы берём из /usr/lib/update-notifier/apt-check (тот самый счётчик, который показывает MOTD при входе на Ubuntu), а список — из apt list --upgradable с фильтром по репозиторию -security. Как страховку запускаем unattended-upgrades --dry-run, чтобы увидеть, что именно было бы установлено автоматически.

Сохраните как /usr/local/bin/notifly-apt-updates:

#!/usr/bin/env bash
set -eu
set -a; source /etc/notifly.env; set +a
# Присылать ли спокойное "всё актуально", когда обновлять нечего
NOTIFY_ALL_CLEAR="${NOTIFY_ALL_CLEAR:-1}"
HOST=$(hostname -s)
# Обновляем индекс пакетов (без установки)
apt-get update -qq 2>/dev/null || true
# apt-check пишет "обновления;security-обновления" в stderr — например "12;4"
if [ -x /usr/lib/update-notifier/apt-check ]; then
read -r TOTAL SECURITY < <(/usr/lib/update-notifier/apt-check 2>&1 | tr ';' ' ')
else
# Запасной способ, если update-notifier-common не установлен
TOTAL=$(apt list --upgradable 2>/dev/null | grep -c '/' || true)
SECURITY=$(apt list --upgradable 2>/dev/null | grep -Ec '\-security' || true)
fi
TOTAL="${TOTAL:-0}"
SECURITY="${SECURITY:-0}"
# Список именно security-пакетов (первые 15 строк для тела сообщения)
SEC_LIST=$(apt list --upgradable 2>/dev/null \
| grep -E '\-security' \
| awk -F/ '{print "• " $1}' \
| head -n 15)
# Сколько пакетов реально поставил бы unattended-upgrades
UU_COUNT=0
if command -v unattended-upgrades >/dev/null 2>&1; then
UU_COUNT=$(unattended-upgrades --dry-run 2>/dev/null \
| grep -Ec '^\s+' || true)
fi
if [ "$SECURITY" -gt 0 ]; then
# 🔒 Есть обновления безопасности — напоминаем настойчивее
PRIO=7
[ "$SECURITY" -le 2 ] && PRIO=6
/usr/local/bin/notifly-send \
"🔒 $SECURITY security-обновлений — $HOST" \
"Всего к обновлению: $TOTAL. unattended-upgrades поставил бы: $UU_COUNT.
$SEC_LIST" "$PRIO"
elif [ "$TOTAL" -gt 0 ]; then
# 📦 Только обычные обновления — тихий дайджест
/usr/local/bin/notifly-send \
"📦 Доступно обновлений: $TOTAL$HOST" \
"Security-обновлений нет. Можно запланировать обычный apt upgrade." 3
elif [ "$NOTIFY_ALL_CLEAR" = "1" ]; then
# ✅ Обновлять нечего
/usr/local/bin/notifly-send \
"✅ Всё актуально — $HOST" \
"Все пакеты обновлены, обновлений безопасности нет." 2
fi

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

Окно терминала
sudo chmod +x /usr/local/bin/notifly-apt-updates
Окно терминала
sudo crontab -e

Раз в неделю, в понедельник в 9 утра:

0 9 * * 1 /usr/local/bin/notifly-apt-updates >/dev/null 2>&1

Если хотите узнавать о security-обновлениях быстрее, запускайте ежедневно — скрипт всё равно промолчит, когда обновлять нечего (при NOTIFY_ALL_CLEAR=0):

0 9 * * * NOTIFY_ALL_CLEAR=0 /usr/local/bin/notifly-apt-updates >/dev/null 2>&1

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

[Unit]
Description=Notifly apt updates digest
[Service]
Type=oneshot
ExecStart=/usr/local/bin/notifly-apt-updates

/etc/systemd/system/notifly-apt.timer:

[Unit]
Description=Weekly notifly apt updates digest
[Timer]
OnCalendar=Mon 09:00
Persistent=true
[Install]
WantedBy=timers.target

Активируем:

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

Persistent=true догонит пропущенный запуск, если сервер был выключен в понедельник утром.

На RHEL, Rocky, AlmaLinux и Fedora пакеты и обновления безопасности считаются через dnf. Плагин dnf-plugin-security (в RHEL 8+ уже встроен) умеет фильтровать именно security-advisory:

#!/usr/bin/env bash
set -eu
set -a; source /etc/notifly.env; set +a
NOTIFY_ALL_CLEAR="${NOTIFY_ALL_CLEAR:-1}"
HOST=$(hostname -s)
# check-update возвращает код 100, если есть обновления, 0 — если нет
TOTAL=$(dnf -q check-update 2>/dev/null | grep -Ec '^\S+\s+\S+\s+\S+$' || true)
SECURITY=$(dnf -q check-update --security 2>/dev/null | grep -Ec '^\S+\s+\S+\s+\S+$' || true)
# Читаемый список security-advisory (RHSA/ALSA и т. п.)
SEC_LIST=$(dnf -q updateinfo list security 2>/dev/null \
| awk '{print "• " $1 " " $3}' \
| head -n 15)
TOTAL="${TOTAL:-0}"; SECURITY="${SECURITY:-0}"
if [ "$SECURITY" -gt 0 ]; then
PRIO=7
[ "$SECURITY" -le 2 ] && PRIO=6
/usr/local/bin/notifly-send \
"🔒 $SECURITY security-обновлений — $HOST" \
"Всего к обновлению: $TOTAL.
$SEC_LIST" "$PRIO"
elif [ "$TOTAL" -gt 0 ]; then
/usr/local/bin/notifly-send \
"📦 Доступно обновлений: $TOTAL$HOST" \
"Security-обновлений нет. Можно запланировать обычный dnf upgrade." 3
elif [ "$NOTIFY_ALL_CLEAR" = "1" ]; then
/usr/local/bin/notifly-send \
"✅ Всё актуально — $HOST" \
"Все пакеты обновлены, обновлений безопасности нет." 2
fi

Расписание — то же самое (OnCalendar=Mon 09:00 или недельный cron). Быстрая проверка одной командой без скрипта: dnf updateinfo list security покажет список advisory, dnf check-update --security — сами пакеты.

На Windows-серверах доступные обновления считает модуль PSWindowsUpdate. Использует общую функцию Send-Notifly из шаблона sysadmin/index.

C:\scripts\Notifly-Windows-Updates.ps1
. C:\scripts\Notifly.ps1
# Установка модуля (один раз, от администратора):
# Install-Module PSWindowsUpdate -Force
Import-Module PSWindowsUpdate
$NotifyAllClear = $true
$Host = $env:COMPUTERNAME
# Все ожидающие обновления
$updates = Get-WindowsUpdate -MicrosoftUpdate -ErrorAction SilentlyContinue
$total = ($updates | Measure-Object).Count
# Только критические / security-обновления
$security = $updates | Where-Object {
$_.Categories -match "Security" -or $_.MsrcSeverity -in @("Critical", "Important")
}
$secCount = ($security | Measure-Object).Count
# Первые 15 названий для тела сообщения
$secList = ($security | Select-Object -First 15 |
ForEach-Object { "" + $_.Title }) -join "`n"
if ($secCount -gt 0) {
$prio = if ($secCount -le 2) { 6 } else { 7 }
Send-Notifly -Title "🔒 $secCount security-обновлений — $Host" `
-Message "Всего к обновлению: $total.`n$secList" -Priority $prio
} elseif ($total -gt 0) {
Send-Notifly -Title "📦 Доступно обновлений: $total$Host" `
-Message "Security-обновлений нет. Можно запланировать установку." -Priority 3
} elseif ($NotifyAllClear) {
Send-Notifly -Title "✅ Всё актуально — $Host" `
-Message "Все обновления установлены, критических нет." -Priority 2
}

Регистрация в Task Scheduler (от администратора, раз в неделю от SYSTEM):

Окно терминала
$Action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\scripts\Notifly-Windows-Updates.ps1"
$Trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At 09:00
$Princ = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName "Notifly Windows Updates" `
-Action $Action -Trigger $Trigger -Principal $Princ -Description "Notifly: дайджест обновлений"
  • Уязвимые пакеты не висят месяцами. Раз в неделю приходит напоминание, а при появлении security-обновлений — с более высоким приоритетом, чтобы оно не потерялось среди тихих дайджестов.
  • Один канал на весь парк серверов. В заголовке — hostname, а в теле — сколько всего обновлений и что именно из security. Видно всю картину, не заходя на машины.
  • Разумный уровень шума. Обычные обновления приходят тихо (приоритет 3), а «всё актуально» можно вовсе отключить — уведомления не превращаются в спам.
  • Прикладывать полный вывод apt list --upgradable через поле extras, чтобы видеть весь список, не заходя на сервер.
  • Хранить состояние, как в рецепте про диск, чтобы не повторять один и тот же дайджест, пока список не изменился.
  • Ставить security-обновления автоматически (unattended-upgrades) и слать Notifly только факт установки и необходимость перезагрузки (/var/run/reboot-required).
  • Про приоритеты и звук уведомлений — в разделе про отправку сообщений.