Уведомление о доступных обновлениях безопасности
Пакеты с известными уязвимостями, которые месяцами ждут 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 bashset -euset -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)fiTOTAL="${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-upgradesUU_COUNT=0if 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." 3elif [ "$NOTIFY_ALL_CLEAR" = "1" ]; then # ✅ Обновлять нечего /usr/local/bin/notifly-send \ "✅ Всё актуально — $HOST" \ "Все пакеты обновлены, обновлений безопасности нет." 2fiСделайте исполняемым:
sudo chmod +x /usr/local/bin/notifly-apt-updatesЗапуск по расписанию
Заголовок раздела «Запуск по расписанию»Через cron
Заголовок раздела «Через cron»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Через systemd timer (рекомендуется)
Заголовок раздела «Через systemd timer (рекомендуется)»/etc/systemd/system/notifly-apt.service:
[Unit]Description=Notifly apt updates digest
[Service]Type=oneshotExecStart=/usr/local/bin/notifly-apt-updates/etc/systemd/system/notifly-apt.timer:
[Unit]Description=Weekly notifly apt updates digest
[Timer]OnCalendar=Mon 09:00Persistent=true
[Install]WantedBy=timers.targetАктивируем:
sudo systemctl daemon-reloadsudo systemctl enable --now notifly-apt.timersudo systemctl list-timers notifly-apt.timerPersistent=true догонит пропущенный запуск, если сервер был выключен в
понедельник утром.
Альтернатива: RHEL / dnf
Заголовок раздела «Альтернатива: RHEL / dnf»На RHEL, Rocky, AlmaLinux и Fedora пакеты и обновления безопасности считаются
через dnf. Плагин dnf-plugin-security (в RHEL 8+ уже встроен) умеет
фильтровать именно security-advisory:
#!/usr/bin/env bashset -euset -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." 3elif [ "$NOTIFY_ALL_CLEAR" = "1" ]; then /usr/local/bin/notifly-send \ "✅ Всё актуально — $HOST" \ "Все пакеты обновлены, обновлений безопасности нет." 2fiРасписание — то же самое (OnCalendar=Mon 09:00 или недельный cron). Быстрая
проверка одной командой без скрипта: dnf updateinfo list security покажет
список advisory, dnf check-update --security — сами пакеты.
Windows: PowerShell + Task Scheduler
Заголовок раздела «Windows: PowerShell + Task Scheduler»На Windows-серверах доступные обновления считает модуль
PSWindowsUpdate.
Использует общую функцию Send-Notifly
из шаблона sysadmin/index.
. C:\scripts\Notifly.ps1
# Установка модуля (один раз, от администратора):# Install-Module PSWindowsUpdate -ForceImport-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 HighestRegister-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). - Про приоритеты и звук уведомлений — в разделе про отправку сообщений.