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

в 8:02, , рубрики: kpatch, linux, linux kernel, livepatch, selectel, Блог компании Селектел, патчи, селектел, ядро

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://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js