Как пропатчить ядро без перезагрузки: livepatch, kpatch и Canonical Livepatch Service

от автора

pr-3322

Тему обновления патчей ядра без перезагрузки мы уже рассматривали в статье, опубликованной в 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 <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/utsname.h>  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://habrahabr.ru/post/317034/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *