Книга "BPF для моніторингу Linux"

Книга "BPF для моніторингу Linux"Привіт, Хаброжителі! Віртуальна машина BPF – один із найважливіших компонентів ядра Linux. Її грамотне застосування дозволить системним інженерам знаходити збої та вирішувати навіть найскладніші проблеми. Ви навчитеся створювати програми, що відстежують та модифікують поведінку ядра, зможете безпечно впроваджувати код для спостереження подій у ядрі та багато іншого. Девід Калавера та Лоренцо Фонтану допоможуть вам розкрити можливості BPF. Розширте свої знання про оптимізацію продуктивності, мереж, безпеки. — Використовуйте BPF для відстеження та модифікації поведінки ядра Linux. — Впроваджуйте код для безпечного моніторингу подій у ядрі без необхідності перекомпілювати ядро ​​або перезавантажувати систему. — Використовуйте зручні приклади коду на C, Go або Python. - Керуйте ситуацією, володіючи життєвим циклом програми BPF.

Безпека ядра Linux, його можливості та Seccomp

BPF надає потужний спосіб розширення ядра без шкоди для стабільності, безпеки та швидкості. Тому розробники ядра подумали, що було б непогано використовувати його універсальність для поліпшення ізоляції процесів у Seccomp шляхом реалізації фільтрів Seccomp, підтримуваних програмами BPF, відомими також як Seccomp BPF. У цьому розділі ми розповімо, що таке Seccomp і як він застосовується. Потім ви дізнаєтесь, як писати фільтри Seccomp за допомогою програм BPF. Після цього розглянемо вбудовані пастки BPF, які є у ядрі для модулів безпеки Linux.

Модулі безпеки Linux (LSM) – це платформа, що надає набір функцій, які можна використовувати для стандартизованої реалізації різних моделей безпеки. LSM може використовуватися безпосередньо в дереві вихідного коду ядра, наприклад Apparmor, SELinux та Tomoyo.

Почнемо з обговорення можливостей Linux.

можливості

Суть можливостей Linux полягає в тому, що вам потрібно надати непривілейованому процесу дозвіл на виконання певного завдання, але без використання suid для цієї мети, або іншим чином зробити процес привілейованим, зменшуючи можливість атак і забезпечуючи можливість виконання певних завдань. Наприклад, якщо вашому додатку потрібно відкрити привілейований порт, скажімо, 80, замість запуску процесу від імені root можете просто надати йому можливість CAP_NET_BIND_SERVICE.

Розглянемо програму Go з ім'ям main.go:

package main
import (
            "net/http"
            "log"
)
func main() {
     log.Fatalf("%v", http.ListenAndServe(":80", nil))
}

Ця програма обслуговує HTTP сервер на порті 80 (це привілейований порт). Зазвичай ми запускаємо її одразу після компіляції:

$ go build -o capabilities main.go
$ ./capabilities

Однак, оскільки ми не надаємо привілеї root, код видасть помилку при прив'язці порту:

2019/04/25 23:17:06 listen tcp :80: bind: permission denied
exit status 1

capsh (засіб управління оболонкою) - це інструмент, який запускає оболонку з певним набором можливостей.

В цьому випадку, як уже говорилося, замість надання повних прав root можна дозволити прив'язку привілейованих портів, надавши можливість cap_net_bind_service поряд з усіма іншими, які вже є в програмі. Для цього можемо укласти нашу програму в capsh:

# capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' 
   --keep=1 --user="nobody" 
   --addamb=cap_net_bind_service -- -c "./capabilities"

Трохи розберемося у цій команді.

  • capsh - використовуємо capsh як оболонку.
  • -caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' — оскільки нам потрібно змінити користувача (ми не хочемо запускатися з правами root), вкажемо cap_net_bind_service і можливість фактично змінити ідентифікатор користувача з root на nobody, а саме cap_net_bind_service.
  • -keep=1 - хочемо зберегти встановлені можливості, коли виконано перемикання з облікового запису root.
  • -user="nobody" - кінцевим користувачем, що запускає програму, буде nobody.
  • -addamb=cap_net_bind_service - задаємо очищення пов'язаних можливостей після перемикання з режиму root.
  • - -c "./capabilities" - просто запускаємо програму.

Пов'язані можливості – це особливий вид можливостей, які успадковуються дочірніми програмами, коли поточна програма виконує їх за допомогою execve(). Спадкоємства можуть успадковувати тільки можливості, дозволені як пов'язані, або, іншими словами, як можливості оточення.

Ймовірно, вам цікаво, що означає +eip після вказівки можливості опції caps. Ці прапори використовуються для визначення того, що можливість:

-повинна бути активована (p);

-Доступна для застосування (е);

-може бути успадкована дочірніми процесами (i).

Оскільки ми хочемо використати cap_net_bind_service, потрібно зробити це із прапором e. Потім запустимо оболонку у команді. В результаті запуститься двійковий файл capabilities і нам потрібно позначити його прапором i. Нарешті ми хочемо, щоб можливість була активована (ми це зробили, не змінюючи UID) за допомогою p. Це виглядає як cap_net_bind_service+eip.

Ви можете перевірити результат за допомогою ss. Трохи скоротимо висновок, щоб він помістився на сторінці, але він покаже зв'язаний порт та ідентифікатор користувача, відмінні від 0, у даному випадку 65 534:

# ss -tulpn -e -H | cut -d' ' -f17-
128 *:80 *:*
users:(("capabilities",pid=30040,fd=3)) uid:65534 ino:11311579 sk:2c v6only:0

У цьому прикладі ми використали capsh, але ви можете написати оболонку за допомогою libcap. За додатковою інформацією звертайтесь до man 3 libcap.

При написанні програм розробник часто не знає заздалегідь всіх можливостей, необхідних програмі під час виконання; більше, у нових версіях ці можливості можуть змінитися.

Щоб краще зрозуміти можливості нашої програми, можемо взяти інструмент BCC capable, який встановлює kprobe для функції ядра cap_capable:

/usr/share/bcc/tools/capable
TIME      UID  PID   TID   COMM               CAP    NAME           AUDIT
10:12:53 0 424     424     systemd-udevd 12 CAP_NET_ADMIN         1
10:12:57 0 1103   1101   timesync        25 CAP_SYS_TIME         1
10:12:57 0 19545 19545 capabilities       10 CAP_NET_BIND_SERVICE 1

Домогтися того ж ми можемо, використовуючи bpftrace з однорядковим kprobe у функції ядра cap_capable:

bpftrace -e 
   'kprobe:cap_capable {
      time("%H:%M:%S ");
      printf("%-6d %-6d %-16s %-4d %dn", uid, pid, comm, arg2, arg3);
    }' 
    | grep -i capabilities

Це виведе щось на зразок наступного, якщо можливості нашої програми будуть активовані після kprobe:

12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 10 1

П'ятий стовпець — це можливості, яких потребує процес, і, оскільки ці вихідні дані включають, зокрема, неаудитні події, ми бачимо всі неаудитні перевірки і, нарешті, необхідну можливість з прапором аудиту (останній у висновку), встановленим у 1. Можливість, яка нас цікавить, - CAP_NET_BIND_SERVICE, вона визначається як константа у вихідному коді ядра у файлі include/uapi/linux/ability.h з ідентифікатором 10:

/* Allows binding to TCP/UDP sockets below 1024 */
/* Allows binding to ATM VCIs below 32 */
#define CAP_NET_BIND_SERVICE 10<source lang="go">

Можливості часто використовуються під час виконання контейнерів, таких як runC або Docker, щоб вони працювали в непривілейованому режимі, але їм були дозволені лише ті можливості, які необхідні для запуску більшості програм. Коли програма потребує певних можливостей, в Docker можна забезпечити їх за допомогою —cap-add:

docker run -it --rm --cap-add=NET_ADMIN ubuntu ip link add dummy0 type dummy

Ця команда надасть контейнеру можливість CAP_NET_ADMIN, що дозволить налаштувати мережеве посилання для додавання інтерфейсу dummy0.

У наступному розділі показано використання таких можливостей як фільтрація, але за допомогою іншого методу, який дозволить нам програмно реалізувати власні фільтри.

Seccomp

Seccomp означає Secure Computing, це рівень безпеки, реалізований у ядрі Linux, що дозволяє розробникам фільтрувати певні системні виклики. Хоча Seccomp можна порівняти з можливостями Linux, його здатність керувати певними системними викликами робить його набагато гнучкішим порівняно з ними.

Seccomp та можливості Linux не виключають один одного, їх часто використовують разом, щоб отримати користь від обох підходів. Наприклад, ви можете захотіти надати процесу можливість CAP_NET_ADMIN, але не дозволити йому приймати з'єднання через сокет, блокуючи системні виклики accept і accept4.

Спосіб фільтрації Seccomp заснований на фільтрах BPF, що працюють у режимі SECCOMP_MODE_FILTER, і фільтрація системних викликів виконується так само, як і для пакетів.

Фільтри Seccomp завантажуються за допомогою prctl через операцію PR_SET_SECCOMP. Ці фільтри мають форму BPF, яка виконується для кожного пакета Seccomp, представленого за допомогою структури seccomp_data. Ця структура містить еталонну архітектуру, покажчик інструкцій процесора під час системного виклику та максимум шість аргументів системного виклику, виражених як uint64.

Ось як виглядає структура seccomp_data з вихідного коду ядра у файлі linux/seccomp.h:

struct seccomp_data {
int nr;
      __u32 arch;
      __u64 instruction_pointer;
      __u64 args[6];
};

Як видно з цієї структури, ми можемо фільтрувати за системним викликом, його аргументами чи їх комбінацією.

Після отримання кожного пакету Seccomp фільтр повинен виконати обробку, щоб ухвалити остаточне рішення, і повідомити ядру, що робити далі. Остаточне рішення виражається одним із значень, що повертаються (кодів стану).

— SECCOMP_RET_KILL_PROCESS — завершення всього процесу відразу після фільтрації системного виклику, який через це не виконується.

— SECCOMP_RET_KILL_THREAD — завершення поточного потоку відразу після фільтрації системного дзвінка, який через це не виконується.

- SECCOMP_RET_KILL - аліас для SECCOMP_RET_KILL_THREAD, залишений для зворотної сумісності.

— SECCOMP_RET_TRAP — системний виклик заборонений, і сигнал SIGSYS (Bad System Call) відправляється завданням, що викликає його.

— SECCOMP_RET_ERRNO — системний виклик не виконується, і частина значення фільтра SECCOMP_RET_DATA, що повертається, передається в простір користувача як значення errno. Залежно від причини помилки повертаються різні значення errno. Список номерів помилок наведено у наступному розділі.

— SECCOMP_RET_TRACE — використовується для сповіщення трасувальника ptrace за допомогою — PTRACE_O_TRACESECCOMP для перехоплення, коли виконується системний виклик, щоб бачити та контролювати цей процес. Якщо трасувальник не підключений, повертається помилка, errno встановлюється -ENOSYS, а системний виклик не виконується.

— SECCOMP_RET_LOG — системний виклик дозволено та зареєстровано в журналі.

- SECCOMP_RET_ALLOW - системний виклик просто дозволено.

ptrace - це системний виклик для реалізації механізмів трасування в процесі, що називається tracee, з можливістю спостереження та контролю за виконанням процесу. Програма трасування може ефективно впливати на виконання та змінювати регістри пам'яті tracee. У контексті Seccomp ptrace використовується, коли запускається кодом стану SECCOMP_RET_TRACE, отже, трасувальник може запобігти виконанню системного виклику та реалізувати власну логіку.

Помилки Seccomp

Час від часу, працюючи з Seccomp, ви стикатиметеся з різними помилками, які ідентифікуються повертається значенням типу SECCOMP_RET_ERRNO. Щоб повідомити про помилку, системний виклик seccomp поверне -1 замість 0.

Можливі такі помилки:

— EACCESS — стороні, що викликає, не дозволено робити системний виклик. Зазвичай це відбувається тому, що в неї немає привілеїв CAP_SYS_ADMIN або не встановлено no_new_privs за допомогою prctl (про це поговоримо пізніше);

- EFAULT - передані аргументи (args у структурі seccomp_data) не мають дійсної адреси;

- EINVAL - тут може бути чотири причини:

-запитана операція невідома чи підтримується ядром у поточної конфігурації;

-Вказані прапори недійсні для запитаної операції;

-операція включає BPF_ABS, але є проблеми із зазначеним усуненням, яке може перевищувати розмір структури seccomp_data;

-кількість інструкцій, переданих у фільтр, перевищує максимальну;

- ENOMEM - недостатньо пам'яті для виконання програми;

— EOPNOTSUPP — операція вказала, що з SECCOMP_GET_ACTION_AVAIL дія була доступна, проте ядро ​​не підтримує повернення аргументів;

ESRCH - виникла проблема при синхронізації іншого потоку;

- ENOSYS - немає трасувальника, прикріпленого до дії SECCOMP_RET_TRACE.

prctl — це системний виклик, який дозволяє програмі простору користувача керувати (встановлювати та отримувати) конкретними аспектами процесу, такими як порядковий номер байтів, імена потоків, режим захищених обчислень (Seccomp), привілеї, події Perf і т.д.

Seccomp може здатися вам технологією пісочниці, але це не так. Seccomp – утиліта, яка дозволяє користувачам розробляти механізм пісочниці. Тепер розглянемо, як створюються програми користувацьких взаємодій із використанням фільтра, що викликається безпосередньо системним викликом Seccomp.

Приклад фільтра BPF Seccomp

Тут ми покажемо, як поєднати дві розглянуті раніше дії, а саме:

— напишемо програму Seccomp BPF, яка буде застосовуватися як фільтр з різними кодами повернення залежно від прийнятих рішень;

- Завантажимо фільтр, використовуючи prctl.

Для початку потрібні заголовки зі стандартної бібліотеки та ядра Linux:

#include <errno.h>
#include <linux/audit.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <unistd.h>

Перш ніж намагатися виконати цей приклад, ми повинні переконатися, що ядро ​​скомпільовано з CONFIG_SECCOMP та CONFIG_SECCOMP_FILTER, встановленими у y. На робочій машині це можна перевірити так:

cat /proc/config.gz| zcat | grep -i CONFIG_SECCOMP

Решта коду є функцією install_filter, що складається з двох частин. Перша частина містить наш список інструкцій з фільтрації BPF:

static int install_filter(int nr, int arch, int error) {
  struct sock_filter filter[] = {
    BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, arch))),
    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3),
    BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, nr))),
    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1),
    BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)),
    BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
  };

Інструкції встановлюються за допомогою макросів BPF_STMT та BPF_JUMP, визначених у файлі linux/filter.h.
Пройдемося за інструкціями.

— BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) — система завантажує та накопичує з BPF_LD у формі слова BPF_W, пакетні дані розташовуються з фіксованим зміщенням BPF_ABS.

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) — перевіряє із застосуванням BPF_JEQ, чи є значення архітектури в константі акумулятора BPF_K рівним arch. Якщо це так, переходить зі зміщенням 0 до наступної інструкції, інакше, щоб видати помилку, перестрибне зі зміщенням 3 (в даному випадку), тому що arch не збігається.

— BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) — завантажує та накопичує з BPF_LD у формі слова BPF_W, яке є номером системного виклику, що міститься у фіксованому зміщенні BPF_ABS.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) - порівнює номер системного виклику зі значенням змінної nr. Якщо вони рівні, переходить до наступної інструкції та забороняє системний виклик, інакше дозволяє системний виклик із SECCOMP_RET_ALLOW.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)) - завершує програму з BPF_RET і в результаті видає помилку SECCOMP_RET_ERRNO з номером зі змінною err.

— BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) — завершує програму з BPF_RET і дозволяє здійснювати системний дзвінок за допомогою SECCOMP_RET_ALLOW.

SECCOMP - ЦЕ CBPF
Можливо, вам цікаво, чому замість скомпільованого об'єкта ELF або програми на C, скомпільованої з JIT, використовується список інструкцій.

На це є дві причини.

• По-перше, Seccomp застосовує cBPF (класичний BPF), а не eBPF, що означає: він не має реєстрів, а є лише акумулятор для зберігання останнього результату обчислень, як можна побачити в прикладі.

• По-друге, Seccomp приймає вказівник на масив інструкцій BPF прямо і нічого більше. Макроси, які ми використовували, лише допомагають вказати ці інструкції у зручній для програмістів формі.

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

if (arch != AUDIT_ARCH_X86_64) {
    return SECCOMP_RET_ALLOW;
}
if (nr == __NR_write) {
    return SECCOMP_RET_ERRNO;
}
return SECCOMP_RET_ALLOW;

Після визначення коду фільтра у структурі socket_filter потрібно визначити sock_fprog, що містить код та обчислену довжину фільтра. Ця структура даних необхідна як аргумент для оголошення роботи процесу надалі:

struct sock_fprog prog = {
   .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
   .filter = filter,
};

Залишилося лише одне, що потрібно зробити у функції install_filter, – завантажити саму програму! Для цього використовуємо prctl, взявши PR_SET_SECCOMP як опцію, щоб увійти в режим захищених обчислень. Потім вкажемо режим завантажувати фільтр за допомогою SECCOMP_MODE_FILTER, який міститься в змінній prog типу sock_fprog:

  if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
    perror("prctl(PR_SET_SECCOMP)");
    return 1;
  }
  return 0;
}

Нарешті, можемо скористатися нашою функцією install_filter, але перед цим потрібно задіяти prctl, щоб встановити PR_SET_NO_NEW_PRIVS для поточного виконання і тим самим уникнути ситуації, коли дочірні процеси набувають ширших привілеїв, ніж батьківські. При цьому ми можемо робити наступні виклики prctl у функції install_filter без прав root.

Тепер можна викликати функцію install_filter. Заблокуємо всі системні виклики write, що стосуються архітектури X86-64, і просто дамо дозвіл, що блокує всі спроби. Після встановлення фільтра продовжуємо виконання, використовуючи перший аргумент:

int main(int argc, char const *argv[]) {
  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
   perror("prctl(NO_NEW_PRIVS)");
   return 1;
  }
   install_filter(__NR_write, AUDIT_ARCH_X86_64, EPERM);
  return system(argv[1]);
 }

Приступимо. Для компіляції нашої програми ми можемо використовувати або clang, або gcc, у будь-якому випадку це просто компіляція файлу main.c без спеціальних опцій:

clang main.c -o filter-write

Як було зазначено, ми заблокували усі записи у програмі. Щоб перевірити це, потрібна програма, яка щось виводить — ls здається хорошим кандидатом. Ось як вона зазвичай поводиться:

ls -la
total 36
drwxr-xr-x 2 fntlnz users 4096 Apr 28 21:09 .
drwxr-xr-x 4 fntlnz users 4096 Apr 26 13:01 ..
-rwxr-xr-x 1 fntlnz users 16800 Apr 28 21:09 filter-write
-rw-r--r-- 1 fntlnz users 19 Apr 28 21:09 .gitignore
-rw-r--r-- 1 fntlnz users 1282 Apr 28 21:08 main.c

Прекрасно! Ось як виглядає використання нашої програми-оболонки: ми просто передаємо програму, яку хочемо протестувати, як перший аргумент:

./filter-write "ls -la"

Після виконання ця програма видає абсолютно порожній висновок. Тим не менш, ми можемо застосувати strace, щоб побачити, що відбувається:

strace -f ./filter-write "ls -la"

Результат роботи дуже скорочений, але відповідна його частина показує, що записи блокуються з помилкою EPERM - тієї самої, яку ми налаштували. Це означає, що програма нічого не виводить, тому що не може отримати доступ до системного виклику write:

[pid 25099] write(2, "ls: ", 4) = -1 EPERM (Operation not permitted)
[pid 25099] write(2, "write error", 11) = -1 EPERM (Operation not permitted)
[pid 25099] write(2, "n", 1) = -1 EPERM (Operation not permitted)

Тепер ви розумієте, як працює Seccomp BPF і добре уявляєте, що можна зробити з його допомогою. Але хіба не хотілося б досягти того ж за допомогою eBPF замість cBPF, щоб використати всю його міць?

Розмірковуючи про програми eBPF, більшість людей думає, що вони просто пишуть їх та завантажують із привілеями адміністратора. Хоча це твердження загалом вірне, ядро ​​реалізує набір механізмів захисту об'єктів eBPF різних рівнях. Ці механізми називаються пастки BPF LSM.

Пастки BPF LSM

Щоб забезпечити незалежний від архітектури контроль системних подій LSM реалізує концепцію пасток. Технічно виклик пастки схожий на системний виклик, проте незалежний від системи та інтегрований з інфраструктурою. LSM надає нову концепцію, в якій рівень абстракції здатний допомогти уникнути проблем, що виникають у ході роботи із системними викликами на різних архітектурах.

На момент написання книги ядро ​​мало сім пасток, пов'язаних з BPF-програмами, і SELinux - єдиний вбудований LSM, що їх реалізує.

Вихідний код пасток розміщений у дереві ядра у файлі include/linux/security.h:

extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size);
extern int security_bpf_map(struct bpf_map *map, fmode_t fmode);
extern int security_bpf_prog(struct bpf_prog *prog);
extern int security_bpf_map_alloc(struct bpf_map *map);
extern void security_bpf_map_free(struct bpf_map *map);
extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
extern void security_bpf_prog_free(struct bpf_prog_aux *aux);

Кожна з них буде викликатись на різних етапах виконання:

- security_bpf – проводить початкову перевірку виконаних системних викликів BPF;

- security_bpf_map - перевіряє, коли ядро ​​повертає файловий дескриптор для картки;

— security_bpf_prog — перевіряє, чи ядро ​​повертає дескриптор файлу для програми eBPF;

— security_bpf_map_alloc — перевіряє, чи ініціалізоване поле безпеки всередині карток BPF;

— security_bpf_map_free — перевіряє, чи здійснюється очищення поля безпеки всередині карток BPF;

- security_bpf_prog_alloc - перевіряє, чи ініціалізується поле безпеки всередині BPF-програм;

- security_bpf_prog_free - перевіряє, чи очищається поле безпеки всередині BPF-програм.

Тепер, бачачи все це, ми розуміємо: ідея перехоплювачів LSM BPF полягає в тому, що вони можуть забезпечити захист кожного об'єкта eBPF, гарантуючи, що тільки ті, які мають відповідні привілеї, можуть виконувати операції над картами та програмами.

Резюме

Безпека - це не те, що ви можете впровадити універсальним способом для всього, що хочете вберегти. Важливо мати можливість захищати системи на різних рівнях та у різний спосіб. Хочете вірте, хочете ні, найкращий спосіб убезпечити систему — організовувати різні рівні захисту з різних позицій, щоб зниження безпеки одного рівня не дозволило отримати доступ до всієї системи. Розробники ядра проробили велику роботу, надавши нам набір різних верств та точок взаємодії. Ми сподіваємося, що дали вам гарне уявлення про те, що таке шари та як використовувати програми BPF для роботи з ними.

Про авторів

Девід Калавера є технічним директором у Netlify. Він працював у службі підтримки Docker та брав участь у розробці інструментів Runc, Go та BCC, а також інших проектів з відкритим вихідним кодом. Відомий своєю роботою над проектами Docker та розвитком екосистеми плагінів Docker. Девід дуже любить флейм-графи і завжди прагне оптимізації продуктивності.

Лоренцо Фонтану працює в команді розробників ПЗ з відкритим вихідним кодом у Sysdig, де в основному займається Falco - проектом Cloud Native Computing Foundation, який забезпечує безпеку середовища виконання контейнерів та виявлення аномалій через модуль ядра та eBPF. Він захоплений розподіленими системами, програмно обумовленими мережами, ядром Linux та аналізом продуктивності.

» Докладніше з книгою можна ознайомитись на сайті видавництва
» Зміст
» уривок

Для Хаброжителів знижка 25% купона Linux

За фактом оплати паперової версії книги на e-mail надсилається електронна книга.

Джерело: habr.com

Додати коментар або відгук