Як пропатчити ядро без перезавантаження: 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 патч!",
utsname()->sysname,
utsname()->release,
utsname()->version);
return 0;
}


Створимо патч з допомогою команди:

$ 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 патч!",
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
log 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 патч! версія 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


&Nbsp;завершення встановлення вийдемо з системи, потім знову увійдемо і виконаємо:

$ sudo canonical-livepatch enable [токен]


Якщо все було зроблено правильно, ми отримаємо наступне повідомлення:

Successfully enabled device. Using machine-token: [токен]


Далі виконаємо команду:

$ canonical-status livepatch

kernel: 4.4.0-47.68-generic
fully-патч: true
версія: "14.1"


Висновок показує, що сanonical-livepatch працює, і в ядро встановлені всі останні оновлення. Більш детальну інформацію можна отримати, скориставшись опцією --verbose:

$ canonical-livepatch status --verbose


client-version: "6"
machine-id: [id]
machine-token:[token]
architecture: x86_64
cpu-model: Intel® Xeon® CPU E5-2670 v3 @ 2.30 GHz
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
версія: "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. Природно, що всі аспекти теми у рамках однієї публікації неможливо торкнутися. Якщо у вас є зауваження і доповнення   ласкаво просимо до коментарі.

А якщо ви хочете більш глибоко вивчити тему, зверніть увагу на такі посилання:



Якщо ви з якихось причин не можете залишати коментарі тут — ласкаво просимо в наш корпоративний блог.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.