Как пропатчить ядро без перезагрузки: livepatch, kpatch и Canonical Livepatch Service
Тему обновления патчей ядра без перезагрузки мы уже рассматривали в статье, опубликованной в 2014 году. В ней речь шла о KernelCare — инструменте, разработанном нашими партнёрами из компании Cloud Linux. На момент написания статьи KernelCare был чуть ли не единственным пригодным для полноценного использования инструментом для наложения патчей.
Прошло два с небольшим года — и ситуация изменилась, причём кардинально: начиная с версии 4.0 возможность наложения патчей «на лету» была официально добавлена в ядро.
Инструменты kpatch и kGraft, которые в 2014 году находились в «сыром» состоянии, также были существенно усовершенствованы. Kpatch даже был добавлен в официальные репозитории, — например, в Ubuntu 16.04 его уже можно установить с помощью стандартного менеджера пакетов.
А компания Canonical совсем недавно представила сервис Canonical Livepatch Service, с помощью которого можно патчить без перезагрузки ядро Ubuntu.
Более подробно о некоторых современных инструментах для добавления патчей мы расскажем в этой статье.
Простейший пример: livepatch
Начнём с очень простого эксперимента. Для этого нам понадобится любой дистрибутив Linux c ядром версии 4.0 или выше (в нашем случае это Ubuntu 16.04; здесь и далее все примеры команд приводятся именно для этого дистрибутива). В новых версиях ядра функция добавления патчей «на лету»(она так и называется — livepatch) включена по умолчанию.
Чтобы проверить, как она работает, нам потребуется, во-первых, установить заголовки ядра:
$ sudo apt-get install linux-headers-$(uname -r)
Далее установим отладочные символы ядра:
#добавляем репозитории
$ codename=$(lsb_release -sc)
$ sudo tee /etc/apt/sources.list.d/ddebs.list << EOF
deb http://ddebs.ubuntu.com/ ${codename} main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-security main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-proposed main restricted universe multiverse
EOF
#добавляем ключ
wget -Nq http://ddebs.ubuntu.com/dbgsym-release-key.asc -O- | sudo apt-key add -
#обновляем список пакетов
$ sudo apt-get update
#устанавливаем отладочные символы
$ sudo apt-get install linux-image-$(uname -r)-dbgsym
Далее выполним:
$ sudo apt-get build-dep linux
При выполнении этой команды в Ubuntu 16.04 может быть выдано следующее сообщение об ошибке:
E: You must put some 'source' URIs in your sources.list
Причина ошибки в том, что репозитории deb-src по умолчанию не подключены, а соответствующие строки в файле /etc/apt/sources.list закомментированы. Чтобы мы смогли работать с репозиториями исходных кодов, выполним:
$ sudo sed -i -- 's/#deb-src/deb-src/g' /etc/apt/sources.list && sudo sed -i -- 's/# deb-src/deb-src/g' /etc/apt/sources.list
$ sudo apt-get update
После этого предыдущая команда будет выполняться без ошибок.
К эксперименту всё готово, можно начинать:
wget http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/samples/livepatch/livepatch-sample.c
Мы скачали код модуля ядра, который вносит изменения в основной ядерный код и модифицирует вывод команды cat /proc/cmdline. Теперь этот самый модуль нужно собрать. Для этого создадим следующий make-файл:
obj-m +=livepatch-sample.o
KDIR= /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
rm -rf *.o *.ko *.mod.* .c* .t*
Соберём модуль и вставим его в ядро:
$ make
$ insmod livepatch-sample.ko
Посмотрим, что получилось. Выполним:
$ cat /proc/cmdinfo
Вместо стандартной информации о параметрах ядра мы увидим вот такой текст:
this has been live patched
Как видим, патч был успешно применён.
Вся информация о загруженных патчах хранится в директории /sys/kernel/livepatch:
$ ls /sys/kernel/livepatch/
livepatch_sample
Деактивировать патч можно с помощью команды:
$ echo 0 > /sys/kernel_livepatch/livepatch_sample/enabled
Kpatch
Kpatch — инструмент, разработанный компаний Red Hat. Впервые он был представлен широкой пользовательской аудитории в феврале 2016 года. За это время он был значительно усовершенствован: в Ubuntu 16.04 он уже включён в официальные репозитории. Рассмотрим особенности работы с kpatch на практических примерах.
Начнём с установки необходимых зависимостей:
$ sudo apt-get install libelf-dev dpkg-dev
Для полноценной работы с kpatch также желательно установить ccache:
$ sudo apt-get install ccache
$ ccache --max-size=5G
Вот и всё, зависимости установлены. Можно устанавливать kpatch:
$ sudo apt-get install kpatch kpatch-build
В нашем эксперименте мы будем патчить исходники ядра. Клонируем репозиторий с исходным кодом нашей текущей версии Ubuntu:
git clone git://kernel.ubuntu.com/ubuntu/ubuntu-xenial.git
По завершении клонирования скопируем исходники в директорию ubuntu-xenial-kpatch (это нужно, чтобы вносить изменения в исходный код и потом создавать на основе этих изменений патчи):
$ mkdir ubuntu-xenial-kpatch
$ cp -r ubuntu-xenial ubuntu-xenial-kpatch
Откроем файл ubuntu-xenial-kpatch/ubuntu-xenial/fs/proc/version.c и внесём в него следующие изменения:
#include
#include
#include
#include
#include
#include
static int version_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, linux_proc_banner,
"This has been patched!",
utsname()->sysname,
utsname()->release,
utsname()->version);
return 0;
}
Cоздадим патч с помощью команды:
$ diff -u ubuntu-xenial/fs/proc/version.c ubuntu-xenial.kpatch/ubuntu-xenial/proc.version.c > version.patch
Патч представляет собой обычный текстовый файл, в котором перечислены внесённые изменения:
--- ubuntu-xenial/fs/proc/version.c 2016-12-05 10:04:30.126141156 +0300
+++ ubuntu-xenial.kpatch/ubuntu-xenial/fs/proc/version.c 2016-12-05 10:10:35.678461801 +0300
@@ -8,6 +8,7 @@
static int version_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, linux_proc_banner,
+ "This has been patched!",
utsname()->sysname,
utsname()->release,
utsname()->version);
Чтобы добавить патч в ядро, выполним:
$ kpatch-build -t vmlinux --skip-gcc-check version.patch
WARNING: Skipping gcc version matching check (not recommended)
Debian/Ubuntu distribution detected
Downloading the kernel source for 4.4.0-51-generic
Unpacking kernel source
Testing patch file
checking file fs/proc/version.c
Reading special section data
Building original kernel
Building patched kernel
Detecting changed objects
Rebuilding changed objects
Extracting new and modified ELF sections
version.o: changed function: version_proc_show
Building patch module: kpatch-version.ko
SUCCESS
Как видно из только что приведённого вывода, на выходе мы получаем модуль ядра. Чтобы применить патч, нужно просто добавить этот модуль стандартным способом:
sudo insmod kpatch-version.ko
Посмотрим, что получилось в результате:
cat /proc/version
This has been patched! version Linux (buildd@lcy01-08) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) 4.4.0-51-generic
Всё работает!
Canonical Livepatch Service
Несколько месяцев назад компания Canonical запустила официальный сервис Canonical LivePatch Service, который позволяет патчить ядро «на лету» при помощи простейших команд. Этот сервис ориентирован в первую очередь на пользователей enterprise-уровня, и поэтому является платным.
Но рядовые пользователи тоже могут оперативно получать все свежие обновления ядра. Для этого нужно зарегистрироваться на Ubuntu One и получить токен. Токен даёт возможность установить на 3 машины программу canonical-livepatch, которая загружает и добавляет патчи.
Посмотрим, как работает Canonical Livepatch Service. Перейдём по ссылке выше, получим токен, а далее выполним:
$ sudo snap install canonical-livepatch
По завершении установки выйдем из системы, затем войдём снова и выполним:
$ sudo canonical-livepatch enable [токен]
Если всё было сделано правильно, мы получим следующее сообщение:
Successfully enabled device. Using machine-token: [токен]
Далее выполним команду:
$ canonical-livepatch status
kernel: 4.4.0-47.68-generic
fully-patched: true
version: "14.1"
Вывод показывает, что сanonical-livepatch работает, и в ядро установлены все последние обновления. Более подробную информацию можно получить, воспользовавшись опцией −−verbose:
$ canonical-livepatch status --verbose
client-version: "6"
machine-id: [id]
machine-token:[token]
architecture: x86_64
cpu-model: Intel(R) Xeon(R) CPU E5-2670 v3 @ 2.30GHz
last-check: 2016-12-05T11:56:02.88803394+03:00
boot-time: 2016-11-29T10:48:38+03:00
uptime: 145h18m21s
status:
- kernel: 4.4.0-47.68-generic
running: true
livepatch:
checkState: checked
patchState: applied
version: "14.1"
fixes: |-
* CVE-2016-7425
* CVE-2016-8658
Также информацию об установленных патчах можно получить, заглянув в уже упомянутую выше директорию /sys/kernel/livepatch:
$ ls /sys/kernel/livepatch
kpatch_livepatch_Ubuntu_4_4_0_47_68_generic_14
Kpatch_livepatch_Ubuntu_4_4_0_47_68_generic_14 — это и есть последний загруженный патч. Последние цифры в имени патча (14) совпадают с номером версии, указанным в выводе команды canonical-livepatch status (см. выше).
Убедиться, что новый патч был добавлен, можно и с помощью команды lsmod:
$ lsmod |grep livepatch
kpatch_livepatch_Ubuntu_4_4_0_47_68_generic_14 36864 1
Заключение
В этой статье мы проделали небольшой отбор инструментов для добавления патчей в ядро Linux. Естественно, что все аспекты темы в рамках одной публикации затронуть невозможно. Если у вас есть замечания и дополнения — добро пожаловать в комментарии.
А если вы хотите изучить тему более глубоко, обратите внимание на следующие ссылки:
- https://github.com/dynup/kpatch — официальный репозиторий kpatch;
- https://events.linuxfoundation.org/sites/events/files/slides/kpatch-linuxcon_3.pdf — презентация доклада о kpatch c подробным разъяснением принципов его работы;
- http://events.linuxfoundation.org/sites/events/files/slides/kGraft.pdf — презентация о kGraft — инструменте, созданном разработчиками SUSE (мы в статье его не рассматривали);
- http://www.slideshare.net/UdoSeidel/kpatchkgraft — неплохая презентация о техниках наложения патчей «на лету»
Если вы по каким-то причинам не можете оставлять комментарии здесь — добро пожаловать в наш корпоративный блог.