Серверы, которые никто не обновляет, стареют не как вино - они стареют как незакрытая рана. Уязвимости накапливаются тихо, патчи безопасности выходят и проходят мимо, а администратор в лучшем случае вспоминает про apt upgrade раз в несколько недель. Ручное обновление парка машин - это рутина, которую легко откладывать, пока не становится поздно. Именно для того, чтобы разорвать этот порочный круг, в экосистеме Debian и Ubuntu существует инструмент unattended-upgrades.

Но у автоматики есть своя цена. Бесконтрольное обновление ядра или системных библиотек в разгар рабочего дня, перезагрузка боевого сервера без уведомления, внезапный перезапуск базы данных посреди транзакции - всё это последствия наивной конфигурации "включил и забыл". Настоящая автоматизация - это не "пусть делает что хочет", а чёткая система правил: что обновлять, что не трогать, когда перезагружаться, и кому об этом сообщить.

Установка и первичная активация на Debian и Ubuntu

На Ubuntu начиная с 18-й версии unattended-upgrades присутствует в системе по умолчанию. Тем не менее перед настройкой стоит убедиться, что пакет установлен, а вместе с ним - apt-listchanges, который обеспечивает детальные отчёты об изменениях в обновляемых пакетах:

apt install unattended-upgrades apt-listchanges -y

После установки пакета нужно активировать механизм автообновлений. Самый чистый способ - воспользоваться встроенным конфигуратором, который создаёт нужные файлы и корректно регистрирует systemd-таймеры:

dpkg-reconfigure --priority=low unattended-upgrades

В ответ на вопрос "Automatically download and install stable updates?" выбирают "Yes". Именно после этого шага в системе появляется файл /etc/apt/apt.conf.d/20auto-upgrades с базовыми директивами - без него пакет будет установлен, но ничего делать не будет.

На Debian процесс такой же, а на Ubuntu 24.04 systemd-сервис нужно включить отдельно, чтобы он стартовал при загрузке:

systemctl enable --now unattended-upgrades.service

После активации система будет проверять обновления через два встроенных systemd-таймера: apt-daily.timer отвечает за обновление списков пакетов, apt-daily-upgrade.timer - за непосредственную установку. По умолчанию апгрейд запускается примерно через 6-8 минут после старта системы и затем ежедневно около 6 утра, причём с небольшой случайной задержкой - чтобы сотни серверов не ломились на зеркало одновременно. Проверить статус таймеров можно так:

systemctl list-timers | grep apt

Два ключевых конфигурационных файла и их логика

Вся конфигурация unattended-upgrades разделена между двумя файлами, и понимание их ролей критически важно. Первый, /etc/apt/apt.conf.d/20auto-upgrades, управляет расписанием и частотой проверок. Второй, /etc/apt/apt.conf.d/50unattended-upgrades, определяет логику - что обновлять, что исключить, когда перезагружаться и куда слать уведомления.

Файл 20auto-upgrades в типичной конфигурации выглядит лаконично:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";

Числа здесь означают интервал в днях. Единица - это ежедневно, семь - раз в неделю, ноль - функция отключена. AutocleanInterval управляет очисткой кэша скачанных пакетов - семь дней разумный компромисс между занимаемым местом и нагрузкой на диск при очистке.

Основная конфигурация сосредоточена в 50unattended-upgrades. Именно здесь администратор описывает политику обновлений для конкретного сервера. По умолчанию файл разрешает только обновления безопасности - это консервативная и разумная позиция для продакшна. Чтобы добавить все стабильные обновления репозитория, нужно раскомментировать соответствующую строку:

Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}";
    "${distro_id}:${distro_codename}-security";
    // "${distro_id}ESMApps:${distro_codename}-apps-security";
    // "${distro_id}ESM:${distro_codename}-infra-security";
    "${distro_id}:${distro_codename}-updates";
};

Переменные ${distro_id} и ${distro_codename} подставляются автоматически - это удобно при работе с шаблонами конфигурации в Ansible или Chef, когда один файл покрывает разные версии дистрибутива.

Чёрный список пакетов и точечный контроль обновлений

Не всё должно обновляться само. База данных, которую администратор обновляет строго по регламенту с предварительным тестированием, ядро Linux на серверах с нестандартными модулями, специфические версии OpenJDK, которые прибиты к конкретному выпуску - всё это кандидаты в чёрный список. Каждая запись в Package-Blacklist - это регулярное выражение, а не просто имя пакета.

Важно это понять сразу, потому что запись вида "postgresql" заблокирует только пакет с точным именем postgresql, но не postgresql-16, postgresql-client или postgresql-contrib. Правильная маска выглядит иначе:

Unattended-Upgrade::Package-Blacklist {
    "postgresql.*";
    "mysql-server.*";
    "linux-image.*";
    "linux-headers.*";
    "nginx";
    "docker-ce";
};

Строка "linux-image.*" блокирует автоматическое обновление ядра - распространённая практика на серверах, где ядро тестируется перед выкаткой отдельно. Строка "kube.*" одним выражением накроет весь стек Kubernetes, если он установлен вручную и управляется отдельным процессом. Проверить, что регулярное выражение работает так, как задумано, можно через тестовый прогон:

unattended-upgrade --dry-run --debug 2>&1 | grep -E 'blacklist|Packages that'

Флаг --dry-run запускает полный цикл проверки без фактической установки пакетов - удобный способ убедиться в правильности конфигурации перед тем, как она заработает в бою.

Управление перезагрузками - отложить нельзя немедленно

Перезагрузка после обновления ядра или glibc - это не каприз системы, а архитектурная необходимость. Обновлённый код патча безопасности живёт на диске, но в оперативной памяти продолжает работать старое ядро. До перезагрузки уязвимость закрыта только формально. Именно поэтому управление политикой перезагрузок - один из самых ответственных аспектов настройки.

Сигнал о необходимости перезагрузки система оставляет в файле /var/run/reboot-required. Пока этот файл существует, перезагрузка не была выполнена. Проверить его наличие можно в любой момент:

ls -la /var/run/reboot-required 2>/dev/null && echo "Перезагрузка требуется" || echo "Перезагрузка не нужна"

По умолчанию unattended-upgrades не перезагружает сервер автоматически - и это правильное поведение "из коробки". Чтобы включить отложенную перезагрузку в заданное время, в 50unattended-upgrades раскомментируют и настраивают три параметра:

Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";

Здесь каждый параметр несёт свою нагрузку. Automatic-Reboot "true" разрешает перезагрузку только при наличии файла /var/run/reboot-required - то есть только тогда, когда она действительно нужна. Automatic-Reboot-Time задаёт конкретное время - в данном случае три часа ночи, когда нагрузка на большинстве серверов минимальна. Automatic-Reboot-WithUsers "false" добавляет защиту: если в момент запланированной перезагрузки на сервере есть активные SSH-сессии, перезагрузка откладывается. Это предотвращает обрыв чьей-то ночной работы в терминале.

Однако у параметра Automatic-Reboot-Time есть тонкость, которую легко упустить: он ориентируется на системное время сервера. Если серверы стоят в разных локациях и часовой пояс настроен неправильно, перезагрузка произойдёт совсем не тогда, когда ожидалось. Проверить текущий часовой пояс и при необходимости скорректировать:

timedatectl status
timedatectl set-timezone Europe/Moscow

Для сред, где нужна более гибкая политика перезагрузок - например, только по воскресеньям или только в определённое окно обслуживания - правильный подход состоит в создании отдельного systemd-таймера, который проверяет наличие /var/run/reboot-required и инициирует перезагрузку по нужному расписанию. Это элегантнее, чем пытаться уложить сложную логику в параметры unattended-upgrades:

# /etc/systemd/system/reboot-if-required.timer
[Unit]
Description=Reboot if required by pending updates

[Timer]
OnCalendar=Sun *-*-* 04:00:00
Persistent=true

[Install]
WantedBy=timers.target
# /etc/systemd/system/reboot-if-required.service
[Unit]
Description=Reboot if /var/run/reboot-required exists

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'test -f /var/run/reboot-required && /sbin/reboot'
systemctl enable --now reboot-if-required.timer

Такой таймер проверяет наличие флага и перезагружает сервер строго по воскресеньям в 4 утра - независимо от того, когда именно было установлено обновление, потребовавшее перезагрузки.

needrestart - управление перезапуском служб без перезагрузки

Большинство обновлений не требуют полной перезагрузки сервера. Обновился nginx - достаточно перезапустить nginx. Обновилась openssl - нужно перезапустить все процессы, использующие старую версию библиотеки. Этим занимается пакет needrestart, который автоматически определяет, какие службы используют устаревшие версии библиотек, и перезапускает их.

apt install needrestart -y

После установки needrestart интегрируется в процесс unattended-upgrades через хук dpkg и запускается автоматически после каждой установки пакетов. По умолчанию он работает в интерактивном режиме - что совершенно бесполезно для автоматизированных процессов, поскольку в неинтерактивном контексте он просто молчит и ничего не перезапускает.

Для продакшн-серверов нужен автоматический режим. Правильнее всего задать его через отдельный файл в /etc/needrestart/conf.d/, а не редактировать основной конфиг напрямую:

cat > /etc/needrestart/conf.d/auto-restart.conf << 'EOF'
# Режимы: (l)ist only, (i)nteractive, (a)utomatically
$nrconf{restart} = 'a';
EOF

Здесь стоит сделать важную оговорку. В связке needrestart с glibc на Debian 12 и Ubuntu 24.04 есть задокументированный нюанс: при обновлении glibc needrestart может многократно перезапускать sshd по мере установки каждой группы пакетов. Если активных SSH-сессий много, systemd в какой-то момент решит, что sshd ведёт себя аномально - слишком часто рестартует - и остановит его без последующего запуска. Потеря SSH-доступа к серверу не входит в планы никакого администратора.

Защита от этого сценария - явный режим работы, при котором needrestart не трогает sshd во время работы unattended-upgrades. Это достигается дополнением конфига:

cat >> /etc/needrestart/conf.d/auto-restart.conf << 'EOF'
# Не перезапускать sshd автоматически во избежание потери доступа
$nrconf{blacklist_rc} = [
    qr(^ssh\.service$),
];
EOF

После обновлений sshd перезапускается вручную или через запланированную перезагрузку - это безопаснее, чем рисковать потерей удалённого доступа посреди ночного апгрейда.

Мониторинг, уведомления и проверка работы

Автоматическая система, за которой никто не следит - это система, о которой узнают только когда что-то пошло не так. Логи unattended-upgrades хранятся в /var/log/unattended-upgrades/ и содержат полную историю: какие пакеты были обновлены, какие пропущены из-за чёрного списка, были ли ошибки.

Проверить последний цикл обновлений:

tail -50 /var/log/unattended-upgrades/unattended-upgrades.log

Для уведомлений на почту в 50unattended-upgrades задают адрес и политику отправки:

Unattended-Upgrade::Mail "Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.";
Unattended-Upgrade::MailReport "on-change";

Значение on-change означает: письмо приходит только тогда, когда что-то реально изменилось - пакеты обновились или возникла ошибка. Значение only-on-error ещё строже - только при сбоях. Тихое always засорит почтовый ящик ежедневными отчётами о том, что "сегодня ничего не изменилось".

Перед тем как оставить конфигурацию работать самостоятельно, стоит убедиться в корректности синтаксиса и логики через принудительный тестовый запуск:

unattended-upgrade --dry-run --debug

А потом проверить, что таймеры systemd видят конфигурацию и знают, когда следующий запуск:

systemctl list-timers apt-daily.timer apt-daily-upgrade.timer

Вывод покажет время последнего и следующего срабатывания каждого таймера. Если NEXT у apt-daily-upgrade.timer пустой - таймер неактивен, и нужно разбираться почему.

Стратегия для продакшн-серверов

Существует соблазн настроить всё один раз по инструкции и больше не думать об этом. Но unattended-upgrades - не та система, где universal best practice применима ко всем без исключения. Сервер с PostgreSQL, которому нельзя падать даже на секунду, требует иной политики, чем веб-сервер со статикой, который переживёт секундный даунтайм.

Для серверов с базами данных или критической инфраструктурой разумная стратегия такова: автоматически применяются только обновления безопасности, ядро и основные системные библиотеки (libc, openssl) вносятся в чёрный список и обновляются вручную в окно обслуживания. Перезагрузка разрешена автоматически, но строго по расписанию и только при отсутствии активных сессий.

Для менее критичных серверов - разрешить все обновления из стабильного репозитория, автоматические перезапуски служб через needrestart, перезагрузка в ночное окно. Этот режим покрывает большинство уязвимостей без какого-либо участия человека.

Граница между "достаточной автоматизацией" и "слишком много автоматизации" у каждого администратора своя. Но одно верно для всех: сервер, который обновляется регулярно и предсказуемо, надёжнее сервера, которого обновляют раз в квартал в панике после того, как в новостях появилось сообщение о критической уязвимости. unattended-upgrades с правильно настроенной политикой перезагрузок - это именно та разница.