Автоматические обновления: unattended-upgrades, dnf-automatic и планирование перезагрузок
Большинство взломов серверов происходит через давно известные уязвимости, исправление для которых вышло недели и месяцы назад. Причина банальна: никто не обновил. Ручное обновление работает, пока серверов три, а потом про один из них забывают. В этой статье настроим автоматические обновления безопасности на Debian (unattended-upgrades) и Rocky Linux (dnf-automatic), добавим уведомления и спланируем перезагрузки так, чтобы srv-web01 и srv-linux01 в домене itpages.lab.local не уходили в рестарт в самый неудобный момент.
Автоматика не означает «без контроля». Мы выберем, что обновлять сами, а что оставить на ручное окно, и научимся проверять результат. Команды выполняются от root.
unattended-upgrades на Debian
Установка и включение
apt update
apt install -y unattended-upgrades apt-listchanges needrestart mailutils
dpkg-reconfigure -plow unattended-upgrades
systemctl status apt-daily.timer apt-daily-upgrade.timer
cat /etc/apt/apt.conf.d/20auto-upgrades
После включения в файле 20auto-upgrades должны стоять две строки: APT::Periodic::Update-Package-Lists "1"; и APT::Periodic::Unattended-Upgrade "1";. Фактически работу делают два системных таймера: один обновляет списки, второй запускает обновление. Расписание можно посмотреть командой systemctl list-timers 'apt-daily*'.
Что именно обновлять
Главная настройка находится в /etc/apt/apt.conf.d/50unattended-upgrades. Правила лучше писать в отдельный файл с более поздним номером, например 52itpages-unattended, чтобы не конфликтовать с пакетными изменениями:
cat > /etc/apt/apt.conf.d/52itpages-unattended <<'EOF'
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename},label=Debian";
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
};
Unattended-Upgrade::Package-Blacklist {
"postgresql-15";
"linux-image-amd64";
};
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Mail "admin@itpages.lab.local";
Unattended-Upgrade::MailReport "on-change";
Unattended-Upgrade::Automatic-Reboot "false";
EOF
Здесь разрешены обновления только из основного репозитория и из репозитория безопасности. Чёрный список содержит пакеты, обновление которых мы хотим делать вручную: СУБД и ядро. Если в статье про управление пакетами ты зафиксировал версии через apt-mark hold, такие пакеты и так не обновятся, но явный список делает намерение видимым.
Режим MailReport "on-change" присылает письмо, только когда что-то действительно обновилось или произошла ошибка. Не используй always: через неделю письма будут игнорироваться, а важное потеряется в потоке.
Пробный запуск
unattended-upgrade --dry-run --debug 2>&1 | tail -30
unattended-upgrade -v
tail -n 30 /var/log/unattended-upgrades/unattended-upgrades.log
grep -E 'Packages that will be upgraded|Allowed origins' /var/log/unattended-upgrades/unattended-upgrades.log | tail
Ключ --dry-run показывает, что было бы обновлено, и какие источники считаются разрешёнными. Если список разрешённых источников пуст или в нём нет security, правила написаны неверно.
dnf-automatic на Rocky Linux
Настройка и таймер
dnf install -y dnf-automatic
cp /etc/dnf/automatic.conf /root/automatic.conf.orig
sed -i 's/^upgrade_type = .*/upgrade_type = security/' /etc/dnf/automatic.conf
sed -i 's/^apply_updates = .*/apply_updates = yes/' /etc/dnf/automatic.conf
grep -E '^(upgrade_type|apply_updates|download_updates|emit_via)' /etc/dnf/automatic.conf
Параметр upgrade_type = security ограничивает установку обновлениями безопасности (метаданные безопасности в Rocky/RHEL есть). Значение default поставит вообще всё, и это рискованно для рабочего сервера. Важно: для уведомлений укажи способ доставки в секции emitters:
[emitters]
emit_via = email,stdio
[email]
email_from = dnf-automatic@srv-linux01.itpages.lab.local
email_to = admin@itpages.lab.local
email_host = localhost
Теперь включаем нужный таймер. Их три варианта: dnf-automatic-notifyonly.timer (только сообщить), dnf-automatic-download.timer (скачать) и dnf-automatic-install.timer (скачать и установить). Для нашей схемы подходит последний:
systemctl enable --now dnf-automatic-install.timer
systemctl list-timers dnf-automatic-install.timer
systemctl cat dnf-automatic-install.timer
journalctl -u dnf-automatic-install.service -n 30 --no-pager
Своё расписание вместо стандартного
Таймеры по умолчанию запускаются в случайное время утром. Если нужен предсказуемый час, переопредели таймер через drop-in:
systemctl edit dnf-automatic-install.timer
[Timer]
OnCalendar=
OnCalendar=Tue,Thu 04:00
RandomizedDelaySec=30min
Пустая строка OnCalendar= сбрасывает исходное значение, иначе расписания сложатся и таймер сработает чаще, чем ты ожидаешь. Проверить итог можно командой systemctl list-timers. Случайная задержка нужна, чтобы десять хостов не обновлялись разом и не положили внутреннее зеркало на srv-backup01.
Перезагрузки: когда и как
Нужна ли перезагрузка
Ядро и системные библиотеки (glibc, openssl) обновляются на диске сразу, но работающие процессы продолжают использовать старый код. Исправление действует по-настоящему только после перезапуска затронутых сервисов, а для ядра — после перезагрузки. Узнать, нужна ли она:
# Debian
ls -l /var/run/reboot-required 2>/dev/null && cat /var/run/reboot-required.pkgs
needrestart -r l
# Rocky
dnf needs-restarting -r; echo "код возврата: $?"
dnf needs-restarting -s
Команда dnf needs-restarting -r возвращает код 1, если перезагрузка нужна. Это удобно для скриптов и мониторинга. Утилита needrestart на Debian обычно предлагает перезапустить сервисы автоматически, и в неинтерактивном режиме её поведение задаётся в /etc/needrestart/conf.d/ параметром $nrconf{restart} = 'a'; (автоматически) или 'l' (только список).
Автоматическая перезагрузка в окно обслуживания
Для некритичных хостов можно разрешить перезагрузку самому unattended-upgrades, но только в указанное время:
cat >> /etc/apt/apt.conf.d/52itpages-unattended <<'EOF'
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";
Unattended-Upgrade::Automatic-Reboot-Time "04:30";
EOF
Запись в файл с >> допустима только при условии, что ранее ты не задавал Automatic-Reboot "false" в том же файле; иначе в конфигурации будут две противоречащие строки. Проверь итог командой apt-config dump | grep Automatic-Reboot.
Для Rocky нет встроенного механизма перезагрузки в dnf-automatic (есть параметр reboot = when-needed в новых версиях, но поведение зависит от сборки). Надёжнее использовать отдельный таймер systemd с проверкой:
cat > /usr/local/sbin/reboot-if-needed.sh <<'EOF'
#!/bin/bash
if ! dnf needs-restarting -r >/dev/null 2>&1; then
logger -t reboot-if-needed "нужна перезагрузка, выполняю"
/usr/bin/systemctl reboot
fi
EOF
chmod 750 /usr/local/sbin/reboot-if-needed.sh
cat > /etc/systemd/system/reboot-if-needed.service <<'EOF'
[Unit]
Description=Reboot if updates require it
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/reboot-if-needed.sh
EOF
cat > /etc/systemd/system/reboot-if-needed.timer <<'EOF'
[Unit]
Description=Maintenance window reboot
[Timer]
OnCalendar=Wed 04:30
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now reboot-if-needed.timer
Не перезагружать все сразу
Если у тебя два одинаковых веб-сервера за балансировщиком, окна перезагрузки должны быть разными: например, srv-web01 в среду, второй узел в четверг. Для кластера и баз данных автоматические перезагрузки лучше вообще не включать, а выполнять пошагово: вывести узел из балансировки, обновить, перезагрузить, проверить, вернуть. Здесь пригодится утилита kured в Kubernetes или обычный скрипт с блокировкой, но принцип один: по одной машине за раз.
Для обновлений ядра без перезагрузки есть живое патчирование (Canonical Livepatch, KernelCare), но это отдельная тема и чаще платное решение, поэтому для лаборатории достаточно окна обслуживания.
Контроль и откат
Как убедиться, что всё работает
# Debian
grep -E 'INFO|ERROR' /var/log/unattended-upgrades/unattended-upgrades.log | tail -20
grep -A3 'Start-Date' /var/log/apt/history.log | tail -20
systemctl is-active apt-daily-upgrade.timer
# Rocky
dnf history | head -10
journalctl -u dnf-automatic-install.service --since '7 days ago' --no-pager | tail -30
systemctl status dnf-automatic-install.timer
Поставь в мониторинг два сигнала: время последнего успешного запуска обновления (если оно старше недели, что-то сломалось) и признак «ожидает перезагрузки» дольше двух-трёх дней. Простейший вариант — файл с меткой и проверка find /var/log/unattended-upgrades -mtime -7.
Прежде чем доверить автоматике боевой сервер, отработай схему на тестовой машине. Хорошая последовательность: тестовый узел обновляется сразу, рабочие получают те же пакеты через пару дней. Если во внутреннем зеркале на srv-backup01 ты управляешь версиями пакетов, это делается без хитростей: тестовая группа смотрит на свежий срез, продуктивная — на срез недельной давности.
Перед каждым окном обновлений полезно иметь свежую копию: если обновление сломает приложение, у тебя будет к чему вернуться. Откат на dnf делается через dnf history undo, на apt придётся ставить предыдущую версию пакета вручную через apt install имя=версия и временно блокировать его командой apt-mark hold.
Дополнительные соображения
Что не стоит обновлять автоматически
Автоматика хороша для исправлений безопасности в стабильных ветках, но не для всего. Мажорные обновления СУБД, смену версии языка выполнения (PHP, Python, Node.js), обновление контейнерных движков и ядра на узлах с проприетарными драйверами лучше делать руками и с проверкой. Правило простое: если после обновления нужна миграция данных или правка конфигурации, оставь пакет вне автоматики. Для остального автоматическое исправление уязвимости за сутки намного безопаснее, чем ручное через месяц.
Проверка работоспособности после обновления
Обновление без проверки опасно: пакет встал, сервис упал, а ты узнаёшь об этом от пользователей. Добавь проверку состояния после окна обслуживания. Простейший вариант — скрипт, который через несколько минут после обновления проверяет ключевые сервисы и порты, и поднимает тревогу при отклонении:
systemctl is-active nginx postgresql itpages-app.service
curl -fsS -o /dev/null -w '%{http_code}\n' http://192.168.10.20:8080/health
ss -tlnp | grep -E ':(80|443|8080)\b'
systemctl --failed --no-legend
Подключи его к systemd-таймеру на десять минут позже окна обновления и вывод отправляй на почту только при ошибке. Тогда после ночного обновления ты утром знаешь, что всё в порядке, или получаешь точное описание проблемы.
Безопасность самой автоматики
Помни, что автоматические обновления дают репозиториям право менять систему без тебя. Поэтому они должны быть доверенными: официальные зеркала дистрибутива или собственный репозиторий на srv-backup01 с подписанными пакетами. Не включай в автоматику сторонние источники без необходимости. Следи за тем, чтобы в логах не появлялись ошибки проверки подписи: это может быть как просроченный ключ, так и попытка подмены. Периодически просматривай историю обновлений и сверяй её с ожиданиями, особенно после загадочных перезапусков сервисов.
Пример полной схемы для лаборатории
Три роли — три политики
Чтобы всё сказанное стало практикой, разложи хосты по ролям. Тестовый узел обновляется ежедневно и перезагружается сам, ночью в 03:30. Рабочий srv-linux01 получает только исправления безопасности по вторникам и четвергам, перезагрузка по средам в окно 04:30. Критичный srv-web01 получает автоматические обновления безопасности без перезагрузки, а ядро и перезапуск делаются вручную в согласованное окно после проверки на тестовом узле. Такой ступенчатый подход ловит проблемы на безопасных машинах раньше, чем они затронут рабочие.
Дополнительно задай уведомления: итоговое письмо с списком обновлённых пакетов и признаком необходимости перезагрузки. Так в начале рабочего дня ты видишь, что произошло за ночь, не заходя на серверы. Если нужно больше контроля, добавь в мониторинг метрику «количество доступных обновлений безопасности» и тревогу при превышении порога по времени.
Для документации сохрани в репозитории конфигурации сами файлы 52itpages-unattended, automatic.conf и юниты таймеров. Любая правка проходит через ревью, а развёртывание на новый хост занимает минуту: скопировать файлы и включить таймеры. С таким подходом новая машина сразу получает ту же политику безопасности, что и остальные, а ты перестаёшь зависеть от памяти и привычек.
Частые ошибки и как их избежать
- Обновление всего подряд на критичных узлах. Автоматически поставленная новая версия СУБД или ядра может сломать приложение. Ограничивай источники (
security), добавляй критичные пакеты в blacklist или versionlock и проверяй правила через--dry-run. - Обновление есть, а перезагрузки нет. Сервер месяцами работает со старым ядром и старыми библиотеками в памяти, хотя в логе всё зелёное. Проверяй
needs-restartingиreboot-required, а в мониторинге отслеживай возраст ожидающей перезагрузки. - Одинаковое время для всех хостов. Все серверы одновременно ходят за пакетами и одновременно перезагружаются, и вместе с ними падает сервис. Используй
RandomizedDelaySec, разные дни для узлов одной роли и никогда не перезагружай оба узла пары в одно окно.
Что дальше
Администрирование Linux — это набор связанных навыков, вот соседние темы серии: