У договора о доступности есть неудобная честность: он не делает исключений для обновлений ядра. Привычный цикл знаком каждому администратору: пакетный менеджер скачивает свежее ядро, загрузчик пересобирает меню, а фактический переход происходит только после перезагрузки. Если окно обслуживания согласовывают раз в квартал, уязвимость повышения привилегий ждёт закрытия неделями, хотя исправление давно готово. Сервис Canonical Livepatch сужает этот зазор: он применяет исправления безопасности прямо к работающему ядру, без остановки сервисов, без разрыва соединений и без вывода узла из балансировщика. Ниже разобраны внутренняя механика технологии, процедура подключения, средства контроля состояния и ограничения, которые определяют место инструмента в регламенте эксплуатации.
Перезагрузка как самый дорогой этап обновления ядра на нагруженном сервере
Установка обновлённого ядра незаметна и быстра: пакет распаковывается за секунды, образ initramfs пересобирается автоматически, запись в меню загрузчика появляется сама. Дорогой частью цикла оказывается не получение пакета, а переход на него. Команда uname -r до первой перезагрузки продолжает показывать прежнюю версию, и это честный ответ системы: исправление установлено, но не работает.
Арифметика доступности беспощадна к расписанию. Уровень 99,9 процента оставляет около 8,7 часа простоя в год, уровень 99,99 процента - примерно 52 минуты. Одна внеплановая перезагрузка узла со снятием нагрузки способна израсходовать ощутимую долю годового бюджета простоя, поэтому её готовят заранее: согласовывают окно, плавно выводят соединения, дожидаются синхронизации реплик, предупреждают владельцев сервисов. Чем строже требования доступности, тем дороже процедура и тем реже она случается. Возникает ловушка: чем надёжнее инфраструктура, тем больше накапливается отложенных перезагрузок.
Особенно заметно ограничение в контейнерных платформах. Контейнер разделяет ядро хоста, поэтому перезапуск даже всего контейнерного стека не меняет уровень ядра: уязвимость остаётся, пока не перезагружен хост. Быстрая реконструкция подов не решает проблему корня, она лишь обновляет пользовательское окружение поверх той же версии ядра.
С точки зрения регламента уязвимость ядра требует закрытия в срок, зафиксированный договором, а не в срок, удобный инженерам. Так образуется разрыв: исправление уже существует, окно ещё не скоро. Живые патчи созданы именно для этого промежутка. Они не отменяют плановую перезагрузку, а закрывают уязвимость на время до неё.
Механика живых патчей внутри ядра Linux и подмена функций без остановки сервисов
Подсистема живых патчей встроена в само ядро: она появилась в ветке 4.0 как общий итог работы над проектами kpatch и kGraft. В конфигурации ядра она включается опцией CONFIG_LIVEPATCH и опирается на инфраструктуру ftrace, изначально созданную для трассировки вызовов. Проверить готовность конкретного ядра можно одной командой:
grep -i livepatch /boot/config-$(uname -r)
Ответ CONFIG_LIVEPATCH=y говорит, что ядро способно принимать патчи. Сам патч оформлен модулем ядра, внутри которого лежит таблица соответствий: какая функция заменяется и где искать её новую версию. После загрузки модуля подсистема регистрирует точки перехвата через ftrace, и вызовы оригинальных функций начинают попадать в исправленный код. Тот же механизм, который раньше лишь наблюдал за вызовами, здесь перенаправляет их на новые тела функций. Для сервисов это выглядит как обычный рассинхронronный...
Нет, стоп: тут и не было никакой остановки. Соединения живы, дескрипторы открыты, таймеры тикают. Снаружи подмена вообще не видна: приложение продолжает работать и даже не подозревает, что внутри ядра часть функций уже заменена.
Самая тонкая часть - согласованность перехода. Нельзя подменять функцию, пока какой-нибудь поток выполняет её старую копию или спит внутри неё. Подсистема переводит систему в состояние, называемое переходным, и терпеливо дожидается, пока ни один поток не останется внутри заменённых функций, после чего переход завершается. Для редких случаев, когда переход затягивается, у записи патча в sysfs есть атрибут force, но это инструмент расчищения заторов, а не штатная кнопка:
ls /sys/kernel/livepatch/
cat /sys/kernel/livepatch/<patch-name>/transition
cat /sys/kernel/livepatch/<patch-name>/enabled
Ход переходов виден и в журнале ядра:
dmesg | grep -i livepatch
С момента применения первого патча ядро получает отметку taint с кодом K, она отражается в маске /proc/sys/kernel/tainted и в сообщениях ядра. Для аудита это удобно: система открыто признаёт, что работает в изменённом патчами состоянии.
Две детали механизма стоит знать заранее. Первая: патчи выпускаются накопительно, каждый следующий включает все предыдущие исправления и атомарно заменяет предшественника за один переход, поэтому на сервере всегда действует одна актуальная версия, а не наслоение из десятков слоёв. Вторая: если исправлению нужны новые поля в структурах, подсистема умеет прикреплять к существующим объектам теневые переменные, но это инструмент авторов патча, а не администратора.
Подключение клиента Canonical Livepatch от установки пакета до первого применённого патча
Клиент сервиса в Ubuntu распространяется как snap-пакет и привязывается к подписке через токен. Порядок действий на типовом сервере выглядит так:
- установить клиент командой sudo snap install canonical-livepatch;
- получить в консоли подписки токен для машины и сохранить его в хранилище секретов инфраструктуры;
- включить сервис командой sudo canonical-livepatch enable <токен> и дождаться подтверждения;
- проверить базовое состояние командой canonical-livepatch status;
- изучить подробный вывод canonical-livepatch status --verbose с версией ядра и перечнем применённых исправлений;
- при работе через подписку Ubuntu Pro подключить систему командой sudo ua attach <токен подписки>, включить сервис командой sudo ua enable livepatch и сверить итог командой sudo ua status.
В свежих выпусках дистрибутива утилита подписки откликается и на команду pro, поэтому записи pro attach, pro enable livepatch и pro status эквивалентны вариантам с ua. После включения клиент сам сверяется с сервисом по расписанию, участие человека не требуется. Текущие настройки смотрят командой canonical-livepatch config: там виден адрес сервера выдачи в строке remote-server: livepatch.canonical.com и параметры формата патчей. Отключается сервис командой sudo canonical-livepatch disable.
Проверка состояния патчей и наблюдение за службой клиента в повседневной эксплуатации
Рабочая лошадка контроля - команда canonical-livepatch status. Формат вывода немного меняется между версиями клиента, но смысловые блоки устойчивы:
client-version: <version>
last-check: <time>
process-id: <pid>
patch-state: <count> applied
kernel: <kernel-version>
Поле kernel подтверждает, к какой сборке ядра применены исправления, поле patch-state отвечает на главный вопрос: сколько патчей действует прямо сейчас, поле last-check показывает время последней сверки с сервисом. Застрявшая на старой дате проверка - первый признак проблем с сетевой связностью или самой службой. Немедленную сверку запускает команда canonical-livepatch refresh: она полезна сразу после включения сервиса и перед составлением отчётов.
Служба клиента живёт в systemd под именем snap.canonical-livepatch.canonical-livepatchd.service, поэтому привычные вопросы задаются стандартными командами:
systemctl status snap.canonical-livepatch.canonical-livepatchd.service
journalctl -u snap.canonical-livepatch.canonical-livepatchd.service -n 50
snap services canonical-livepatch
Для регулярного контроля пригоден короткий сценарий, который система мониторинга вызовет по расписанию:
#!/bin/bash
out=$(canonical-livepatch status 2>/dev/null)
rc=$?
if [ "$rc" -ne 0 ]; then
echo "LIVEPATCH CRITICAL: client exit code $rc"
exit 2
fi
echo "$out" | grep -q "applied" || {
echo "LIVEPATCH WARNING: no patches reported as applied"
exit 1
}
echo "LIVEPATCH OK"
exit 0
Планировщик справится одной строкой в /etc/crontab:
17 8 * * * root /usr/local/bin/check-livepatch.sh >> /var/log/livepatch-check.log 2>&1
Код возврата 2 удобно трактовать как аварию клиента, код 1 - как подозрительное состояние, когда клиент работает, но не подтверждает ни одного применённого патча. За пустым списком иногда стоит не сбой, а ядро, для которого исправления пока не выпущены: прежде чем поднимать дежурную смену, стоит заглянуть в подробный статус и сверить сборку ядра с перечнем обслуживаемых.
Границы применения живых патчей и случаи когда перезагрузку отложить нельзя
Первое ограничение принципиально: живые патчи несут только исправления безопасности. Новые драйверы, поддержка свежего оборудования, изменения в планировщике и сетевой подсистеме остаются уделом обычных обновлений ядра с перезагрузкой. Живой патч не превращает старое ядро в новое, он точечно закрывает в нём известные дефекты.
Второе ограничение техническое. Перехват работает на уровне функций: подсистема подменяет точку входа, а остальное остаётся прежним. Если компилятор встроил короткую функцию прямо в места вызовов, отдельной точки входа для перехвата нет. Если исправление требует смены формата структур, устойчивых изменений в данных или затрагивает код, который невозможно безопасно перевести на новую версию, уязвимость признаётся непригодной для живого патча. Решение принимается по каждой уязвимости отдельно, и часть исправлений до живых патчей не доходит в принципе: тогда окно перезагрузки остаётся единственным путём.
Третье ограничение - покрытие версий. Патч собирается под конкретную сборку ядра и должен точно совпадать с бинарником на диске. Набор обслуживаемых ядер зависит от версии ядра и от уровня подписки: базовые ядра долгосрочных выпусков закрыты лучше всего, аппаратные ветки и пересобранные вручную ядра - хуже или никак. Бесплатный уровень подписки существует, но ограничен личным использованием и числом машин, поэтому в корпоративной инфраструктуре речь обычно идёт о платном тарифе.
Четвёртое ограничение - накопление. Живой патч закрывает уязвимость, но не обновляет базовую версию ядра. Со временем число действующих исправлений растёт, база под ними стареет, и плановая перезагрузка всё равно требуется: она сворачивает накопленное в свежий бинарник, снимает отметку taint и выгружает отработавшие модули патчей. Чем дольше узел живёт без перезагрузки, тем сложнее объяснять его состояние при аудите.
Пятое ограничение - граница компетенции. Живые патчи не трогают пользовательское окружение: обновления glibc, openssl, баз данных и прикладных сервисов идут своим чередом и по-прежнему требуют перезапусков по своему регламенту. Инструмент закрывает только ядро, и обещать большее он не может.
Живые патчи в отчётах соответствия и расхождения со сканерами уязвимостей
У технологии есть побочный эффект, который впервые застал на месте немало команд: сканеры продолжают показывать проблемы. Сканер уязвимостей сравнивает вывод uname -r с базой известных дефектов, а живой патч не меняет строку версии. Ядро числится уязвимым, хотя исправления применены: автоматизированная проверка видит цифры, а не факты. Честно говоря, именно из-за этого живое патчирование иногда получают по наследству вместе с горой ложных срабатываний.
Доказательством служит подробный статус: canonical-livepatch status --verbose перечисляет применённые патчи и закрываемые ими идентификаторы CVE. Этот вывод стоит прикладывать к карточке изменения и хранить до следующего аудита: спор с отчётом сканера превращается в рутинную сверку. В реестр активов полезно внести факт использования сервиса и перечень исключений по уязвимостям, для которых живой патч не выпущен, чтобы при разборе инцидента не объяснять одному и тому же аудитору одно и то же.
В договорах с жёсткими требованиями доступности обычно зафиксировано время реакции на критичные уязвимости. Живые патчи сокращают его с интервала между окнами обслуживания до часов, но только для пригодных случаев. Отчётность станет честнее, если вести два счётчика: закрыто без перезагрузки и ожидает окна. Такая разбивка показывает реальные возможности инфраструктуры и помогает обосновать размер подписки перед финансовой службой.
Гигиена эксплуатации от календаря перезагрузок до учёта патчей в регламенте
Главный практический вывод скучен и надёжен: живые патчи не отменяют плановые перезагрузки, они меняют ритм. Окно обслуживания остаётся в календаре с прежней периодичностью, а живые патчи закрывают уязвимости между окнами. Пересчёт набора патчей под новое ядро клиент выполняет сам: после перезагрузки в свежую сборку применяются только те исправления, которые ей действительно нужны.
Перезагрузка по графику решает сразу несколько задач: сворачивает накопленные патчи в свежий бинарник, очищает состояние taint, выгружает отработавшие модули и проверяет, что узел вообще умеет стартовать с актуальным ядром. Сервер, не перезагружавшийся годами, копит не только патчи, но и скрытые риски: непроверенные процедуры восстановления, устаревший initramfs, расхождения в конфигурации загрузчика. Живые патчи маскируют часть проблем, но не устраняют их причину.
Регламент стоит дополнить тремя привычками: регулярным мониторингом состояния клиента, автоматической фиксацией применённых патчей в системе учёта изменений и периодической сверкой отчётов сканера со статусом сервиса. Перед плановым циклом полезно записать вывод canonical-livepatch status --verbose в журнал изменения, после перезагрузки - убедиться, что сервис пересчитал набор патчей и доложил о состоянии. Тогда живое патчирование займёт своё место в инструментарии: не замена дисциплине обновлений, а точный инструмент с известными границами, который закрывает уязвимости сразу, пока плановое окно ждёт своего часа.