Как пропатчить ядро без перезагрузки: обзор современных инструментов

Тему обновления патчей ядра без перезагрузки мы уже рассматривали в статье, опубликованной в 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 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
$ sudo apt-get build-dep linux

К эксперименту всё готово, можно начинать. Выполним следующую команду:

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. Естественно, что все аспекты темы в рамках одной публикации затронуть невозможно. Если у вас есть замечания и дополнения — добро пожаловать в комментарии.

А если вы хотите изучить тему более глубоко, обратите внимание на следующие ссылки:

Что еще почитать по теме

T-Rex 30 марта 2021

Что такое SMTP-протокол и как он устроен?

SMTP (Simple Mail Transfer Protocol) — протокол передачи почты. Он был представлен еще в 1982 году, но не теряет актуальности до сих пор. В статье разбираемся, какие задачи решает протокол и как он ра…
T-Rex 30 марта 2021
Владимир Туров 1 сентября 2020

Дело совершенно секретного iPod

Это был обычный серый день в конце 2005 года. Я сидел на рабочем месте и писал код для следующей версии iPod. Вдруг без стука ворвался директор ПО для iPod, начальник моего начальника, и закрыл дверь.
Владимир Туров 1 сентября 2020
T-Rex 21 августа 2020

TrendForce: цены на SSD упадут

Эксперты DRAMeXchange предсказывают значительное падение цен на оперативную память и твердотельные накопители в ближайшее время. Причина — сокращение спроса на чипы для NAND и DRAM.
T-Rex 21 августа 2020

Новое в блоге

Михаил Фомин 24 июня 2022

Docker Swarm VS Kubernetes — как бизнес выбирает оркестраторы

Рассказываем, для каких задач бизнесу больше подойдет Docker Swarm, а когда следует выбрать Kubernetes.
Михаил Фомин 24 июня 2022
Ульяна Малышева 30 сентября 2022

«Нулевой» локальный диск. Как мы запустили облако только с сетевыми дисками и приручили Ceph

Чем хороши сетевые диски и почему именно Ceph, рассказал директор по развитию ядра облачной платформы Иван Романько.
Ульяна Малышева 30 сентября 2022
Валентин Тимофеев 30 сентября 2022

Как проходит онбординг сотрудников ИТО? Что нужно, чтобы выйти на смену в дата-центр

Рассказываем, как обучаем новых сотрудников, какие задачи и испытания проходят инженеры прежде, чем выйти на свою первую смену.
Валентин Тимофеев 30 сентября 2022
T-Rex 28 сентября 2022

Книги по SQL: что почитать новичкам и специалистам

Собрали 6 книг, которые помогут на старте изучения SQL и при углублении в тему.
T-Rex 28 сентября 2022