infra
June 11

Все что вы хотели знать про eBPF, но боялись спросить

Выбери strace, выбери tcpdump, выбери gdb, выбери linux capabilities, выбери ограничение пользователя docker, выбери AppArmor, выбери SeLinux. А я не стал выбирать SeLinux, я выбрал кое-что другое. По какой причине? Да ни по какой, какие могут быть причины, когда есть eBPF!

Дэнни Бойл

0. Введение

За последние несколько лет eBPF приобрел большую популярность в сообществе Linux и за его пределами. В этой статье мы обзорно посмотрим на технологию eBPF.

1. Что такое Berkeley Packet Filter?

Классический BPF позволяет пользовательской программе (например, tcpdump) получать определенные сетевые пакеты на основе некоторого фильтра. Фактически фильтрация выполняется внутри ядра Linux. Это значительно повышает производительность, т.к. в пространство пользователя необходимо копировать только пакеты, которые прошли фильтрацию. eBPF это в свою очередь эволюционное развитие BPF, которое используется для безопасного и эффективного расширения возможностей ядра без необходимости изменения исходного кода ядра или загрузки его модулей.

2. Варианты использования eBPF

eBPF можно использовать где угодно в пространстве ядра или в пространстве пользователя и даже непосредственно на сетевых картах. Возможность выгружать eBPF программы на сетевую карту особенно интересны с точки зрения безопасности. Это означает, что ядро может обрабатывать пакеты только после программы eBPF. Главные варианты использования это инструменты отслеживания, наблюдения, измерения производительности и безопасности. Многие новейшие инструменты обнаружения, мониторинга и отслеживания производительности написаны с использованием eBPF, и получают широкое распространение в облачных средах (в т.ч. Cilium (https://docs.cilium.io/en/stable/overview/intro/) и Tetragon (https://tetragon.io/ )).

Картинка взята с https://ebpf.io

3. eBPF программы, Maps, Events

Инфраструктура eBPF состоит из 3х ключевых парадигм.

Картинка взята с https://embracethered.com/blog/posts/2021/offensive-bpf/
  1. eBPF Программы: обычно пишутся на Си и компилируются (JIT) в байт-код eBPF
  2. Maps: Это разделяемая память, которая имеет специальные структуры данных и определенное API. Maps являются слоем, который связывает ядро и пользовательское пространство. Подробней можно почитать https://www.kernel.org/doc/html/v5.18/bpf/maps.html
  3. Events: программы eBPF управляются событиями и запускаются когда ядро или приложение проходит определенную точку перехвата (т.н. hook point). Есть предопределенные перехватчики (hooks): системные вызовы, точки входа/выхода из функции, точки трассировки ядра, сетевые события и др. А если предопределенных перехватчиков недостаточно для чего-то конкретного, то можно создать зонд (probe). Зонды бывают двух типов: зонды ядра (kprobe) и пользовательские зонды (uprobe). Зондами можно подключиться почти к любому месту ядра или пользовательской программы.

Программы eBPF похожи на гибкие  и легкие модули ядра, но по сравнению с модулями ядра имеют гораздо более высокие гарантии надежности. Специальный Верификатор (Verifier) гарантирует, что они стабильны и не приведут к сбою в операционной системе.

4. Загрузка eBPF программ

eBPF программа загружается в ядро привелигированным пользователем с использованием системного вызова bpf(). Этот системный вызов требует root привилегий или CAP_BPF capability для большинства eBPF программ.

Загрузка программы происходит приблизительно в следующем порядке:

  1. Программа в пользовательском пространстве вызывает системный вызов bpf() используя параметр BPF_PROG_LOAD для загрузки eBPF программы в ядро ОС.
  2. Программа в пространстве пользователя связывает программу eBPF с событиями (events) и/или зондами (probes).
  3. При возникновении события eBPF программа будет запускаться в заданном контексте.

5. Альтернативные варианты использования eBPF

eBPF программы интересны с точки зрения безопасности, т.к. обладают сверхспособностями и вот что они могут:

  1. Подключаться к системным вызовам и вызовам функций пользовательского пространства, например, манипулировать структурами данных пользовательского пространства
  2. Перезаписывать возвращаемые значения системного вызова
  3. Вызывать системный вызов system(), чтобы создать новый процесс
  4. Некоторые eBPF программы можно запускать на аппаратных устройствах (например, сетевых картах)
  5. Атакующая программа eBPF может устанавливаться в качестве rootkit
  6. Использоваться для побега из контейнера

6. Итоги

В данной статье мы кратко коснулись технологии eBPF, а в следующих статьях мы перейдем к практическим примерам использования данной мощной технологии.

Вольный перевод: https://embracethered.com/blog/posts/2021/offensive-bpf/