Gdy treść uciekła z druta na host
Poprzedni odcinek skończył się na wniosku: pasywny wgląd w ruch kończy się razem z SNI, certyfikatem i handshake’em. TLS 1.3 zaszyfrował certyfikat i wyciął dekrypcję ruchu na perymetrze sieci, ECH dołożył SNI, QUIC schował resztę negocjacji, a pinning dobił dekrypcję ruchu. Zostało Ci to, co opisywałem jako ostatnie szczeble drabiny – fingerprint i analiza w czasie – czyli prawdopodobieństwo zamiast pewności. I w tym miejscu pojawia się pytanie, które wtedy zostawiłem otwarte: skoro na „drucie” treści już nie ma, to gdzie jeszcze możemy spojrezć w plaintext? Jeżeli zaczynasz od tego odcinka i coś wydaje Ci się opisane za bardzo skrótowo, zajrzyj do poprzednich odcinków o ECH i ETA.
Odpowiedź jest prostsza, niż się wydaje, i trochę irytująca dla sieciowca: treść nigdy nie znika, ona tylko przestaje być widoczna na drucie. W punkcie, w którym aplikacja przekazuje dane do biblioteki TLS, są one jeszcze zapisane tekstem jawnym – szyfrowanie dzieje się dopiero w środku SSL_write, a odszyfrowanie tuż przed tym, jak SSL_read zwróci dane aplikacji. Czyli na hoście, o jeden krok przed kablem i o jeden krok za nim, istnieje moment, w którym widać wszystko. Żaden ECH ani QUIC tego nie dotyka, bo to warstwa nad całym szyfrowaniem. Problem nie w tym, że danych nie ma – problem w tym, że są w cudzej pamięci, w procesie aplikacji, a nie na interfejsie, który możesz podsłuchać.
I tu do gry wchodzi eBPF. W skrócie: to sposób na wstrzyknięcie własnego, bezpiecznego kodu do jądra systemu, który uruchamia się dokładnie w tych punktach, które Cię interesują – przy wywołaniu funkcji, przy pakiecie wchodzącym na kartę sieciową, przy syscallu. Zamiast podsłuchiwać drut, podpinasz się pod SSL_write wewnątrz hosta i czytasz bufor, zanim zostanie zaszyfrowany. To nie jest obejście kryptografii – to po prostu stanięcie po właściwej stronie szyfrowania. Dla człowieka od sieci brzmi to niemal jak zdrada profesji, bo punkt obserwacji przestaje być „na drucie ” w sieci”, która była Twoją domeną, a przenosi się na endpoint.
Ten odcinek jest o tej przeprowadzce. Najpierw jak eBPF w ogóle działa, bo bez tego reszta wisi w powietrzu. Potem to, co najważniejsze dla całej serii – przechwytywanie TLS jawnym tekstem na hoście. Dalej widoczność ruchu sieciowego z poziomu kernela zamiast z tapa czy mirrora, czyli ta sama telemetria co w poprzednim wpisie, tylko pobrana gdzie indziej. Na koniec dwa narzędzia, których możliwe, że realnie używasz albo zaraz będziesz: eBPF w rodzinie Wiresharka i Cisco Hypershield. A pod spodem przez cały czas jedna myśl: widoczność, którą sieć traciła przez poprzednie odcinki tej serii, wraca – ale za cenę zmiany modelu. Nie wisisz już drucie prowadząc rozmowę kontrolowaną. Jesteś na hoście, z agentem, z uprawnieniami, w jądrze. I to zmienia dużo więcej niż tylko miejsce, z którego patrzysz.
Jak to w ogóle działa – programowalne jądro
Żeby zrozumieć, skąd eBPF bierze swoją moc, trzeba zacząć od tego, czym jest jądro systemu dla reszty świata: zamkniętą skrzynią. Wszystko, co ciekawe – pakiety, wywołania systemowe, dostęp do plików, wywołania funkcji w procesach – przechodzi przez jądro, ale normalnie nie da się tam nic własnego dołożyć bez pisania modułu kernela, a moduł kernela to proszenie się o kłopoty: jeden błąd i cały system pada. eBPF wywraca ten układ. Pozwala wstrzyknąć własny, mały program bezpośrednio do jądra i uruchamiać go przy wybranych zdarzeniach – ale w taki sposób, że nie może zawiesić ani zepsuć systemu. Dostajesz bezpieczny sposób na programowanie zachowania kernela w locie, bez jego przebudowy i bez restartu.
Cały mechanizm opiera się na kilku elementach, które warto znać. Pierwszy to hook points – miejsca, do których program się podpina. Jest ich cała rodzina: kprobe wchodzi na dowolną funkcję w jądrze, uprobe na funkcję w programie w userspace (to będzie klucz do przechwytywania TLS), tracepoint na predefiniowane punkty w kernelu, a XDP i tc na ścieżkę pakietów na karcie sieciowej, zanim jądro w ogóle zdąży je przetworzyć. Drugi element to verifier – i to on jest sercem całej bezpieczności. Zanim program trafi do jądra, verifier analizuje każdą możliwą ścieżkę wykonania i odrzuca wszystko, co mogłoby zaszkodzić: nieskończone pętle, dostęp do pamięci poza dozwolonym obszarem, program, który mógłby się nie zakończyć. Jeśli nie da się udowodnić, że kod jest bezpieczny, po prostu nie zostanie załadowany. Dlatego eBPF jest radykalnie bezpieczniejszy od modułu kernela – tam ufasz programiście, tu ufasz testom.
Trzeci element to maps – struktury danych współdzielone między programem w jądrze a procesem w userspace. Program eBPF nie ma jak sam „wysłać” danych na zewnątrz; zamiast tego zapisuje je do mapy (tablica, hash), a proces w userspace tę mapę odczytuje. To jest kanał, którym to, co zebrałeś w kernelu, wraca do Twojego narzędzia. Czwarty to JIT – po przejściu przez verifier bytecode eBPF jest kompilowany do natywnego kodu maszynowego danej architektury, więc działa z prędkością zbliżoną do zwykłego kodu jądra, a nie interpretowanego skryptu. I piąty, spinający wszystko, to loader w userspace – program (albo biblioteka jak libbpf), który przez syscall bpf() ładuje kod, przechodzi walidację, podpina go pod hook i potem czyta mapy.
Brzmi to na dużo zachodu, ale w praktyce próg wejścia jest zaskakująco niski, bo narzędzia jak bpftrace chowają cały ten proces za jedną linijką. Spójrz, ile trzeba, żeby policzyć, który proces jak często uruchamia nowe programy w systemie:
bpftrace -e 'tracepoint:syscalls:sys_enter_execve { @[comm] = count(); }'
W tej jednej linii są wszystkie elementy z góry. tracepoint:syscalls:sys_enter_execve to hook – podpinamy się pod każde wywołanie execve, czyli każde uruchomienie nowego programu. @[comm] to mapa – hash indeksowany nazwą procesu (comm), a count() zlicza trafienia po stronie jądra, bez przesyłania każdego zdarzenia osobno do userspace. Pod spodem bpftrace skompilował to do bytecode’u, przepuścił przez verifier, zJIT-ował i podpiął – a Ty tego nie widzisz. Gdy przerwiesz polecenie, zawartość mapy wyląduje na ekranie: lista procesów i ile razy każdy coś odpalił. Żadnego modułu kernela, żadnego restartu, zero ryzyka wywrócenia systemu.
eBPF to programowalny punkt obserwacyjny i punkt kontroli wewnątrz jądra, bezpieczny z założenia, komunikujący się z userspace przez mapy. Gdy w następnym punkcie podepniemy się pod SSL_write, zmieni się tylko hook – z tracepoint na uprobe na konkretnej funkcji biblioteki TLS – a cała reszta mechaniki pozostanie dokładnie ta sama. Różnica będzie taka, że zamiast liczyć uruchomienia programów, odczytamy bufor z tekstem jawnym, zanim zdąży się zaszyfrować.
„Gdzie przeniósła się dekrypcja ruchu” – tekst jawny na hoście
Zamiast tracepoint na syscallu podpinamy uprobe pod konkretną funkcję biblioteki TLS – tę, przez którą aplikacja oddaje dane do zaszyfrowania. W OpenSSL to SSL_write, a jej sygnatura mówi wszystko, czego potrzebujemy: dostaje wskaźnik do bufora i jego długość, a wywoływana jest zanim cokolwiek zostanie zaszyfrowane. Czyli w momencie wejścia do SSL_write bufor zawiera czysty tekst. Symetrycznie SSL_read zwraca odszyfrowane dane przychodzące. Podpinając się pod obie, widzisz obie strony rozmowy zapisane w postaci tekstu jawnego.
W bpftrace to jest, dosłownie, jedna linijka:
bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libssl.so.3:SSL_write {
printf("%s\n", str(arg1, arg2));
}'
Rozbijmy to na elementy z poprzedniego punktu. uprobe:.../libssl.so.3:SSL_write to hook – podpinamy się pod wejście do SSL_write w konkretnej bibliotece. arg1 to drugi argument funkcji, czyli wskaźnik do bufora, a arg2 to jego długość. str(arg1, arg2) czyta tę pamięć procesu i wypisuje jako tekst. I tyle – uruchom to, wejdź przeglądarką albo curlem na dowolny adres HTTPS obsługiwany przez tę bibliotekę, a na ekranie zobaczysz nagłówki i treść żądania surowym tekstem, zanim opuściły urządzenie końcowe. Żadnego MITM, żadnego podrabiania CA, żadnego dotykania kryptografii. Po prostu stanęliśmy po właściwej stronie szyfrowania. (if you only knew the power of the dark side 😉 ).
I tu zaczyna się robić pod górkę, bo ta jedna linijka działa pięknie dokładnie w jednym przypadku: aplikacja dynamicznie linkuje OpenSSL/libssl pod znaną ścieżką i znaną nazwą funkcji. Świat realny jest brudniejszy. BoringSSL w Chrome i Androidzie to inna biblioteka z własnymi symbolami. NSS w Firefoksie to jeszcze co innego (libnspr/libssl3). GnuTLS ma swoje funkcje. A najgorszy przypadek to Go, który w ogóle nie używa systemowej libssl – ma własne crypto/tls wkompilowane statycznie w binarkę, więc nie ma żadnego libssl.so, pod który można się podpiąć. Tam trzeba hookować symbole samego Go (crypto/tls.(*Conn).writeRecordLocked i odpowiednik do odczytywania (read)), znać ścieżkę do konkretnej binarki i walczyć z innymi trudnościami. Jedna uprobe nie pokrywa więc „całego TLS na hoście” – pokrywa jedną bibliotekę w jednej wersji.
Dlatego w produkcji nie pisze się tego ręcznie, tylko sięga po narzędzie, które te wszystkie przypadki ma już ograne. Najbardziej do tego zadania nadaje się (moim zdaniem) ecapture – otwarty projekt, który przechwytuje plaintext TLS przez eBPF bez certyfikatu CA, z osobnymi modułami pod OpenSSL, LibreSSL, BoringSSL, GnuTLS, NSS/NSPR oraz Go. W najprostszym wariancie to dosłownie:
sudo capture tls
samo wykryje systemową bibliotekę i zacznie wypisywać plaintext. Ale dla sieciowca najciekawszy jest jego tryb keylog: zamiast zrzucać treść, ecapture wyłuskuje z pamięci klucze sesji (TLS master secrets) i zapisuje je do pliku w formacie SSLKEYLOGFILE. Taki plik wrzucasz do Wiresharka, a on odszyfrowuje normalnie zebrany pcap – i nagle masz pełną, znajomą analizę w narzędziu, którego używasz od lat, tyle że klucze przyszły z hosta.
Zwróć uwagę, co się tu po cichu wydarzyło z perspektywy modelu, bo to jest sedno całej serii. Żeby to zadziałało, Twój kod musi działać na tym samym hoście co aplikacja, z uprawnieniami pozwalającymi czytać pamięć jej procesu – czyli zazwyczaj jako root. To nie jest już pasywny podsłuch na drucie, który aplikacja ignoruje i o którym nie wie. To agent wewnątrz systemu, czytający bufory cudzego procesu. Odzyskałeś pełną treść, której ECH i QUIC Ci odebrały – ale zapłaciłeś za to przeniesieniem punktu obserwacji z sieci na endpoint i założeniem, że masz na tym endpoincie uprzywilejowane uprawnienia. Dla ofensywnych działań to zresztą działa identycznie: ten sam eBPF, który daje Ci widoczność na Twoich serwerach, w rękach atakującego z rootem jest narzędziem do wykradania sekretów z pamięci. Potęga i ryzyko to tu dwie strony tej samej uprobe.
Observability sieci – flow z kernela
W poprzednim wpisie opisywałem, jak zbierasz metadane ruchu: tap albo SPAN port, telemetria wysyłana z routera czy switcha (IDP, SPLT, NetFlow), kolektor, analiza. Cały ten model zakłada, że punkt zbierania danych jest już na drodze pakietu – gdzieś w infrastrukturze, przez którą ruch przepływa. eBPF przewraca to założenie tak samo, jak przy TLS przewrócił założenie o dekrypcji ruchu: zamiast patrzeć na pakiet w locie, patrzysz na niego w jądrze hosta, który go wysyła albo odbiera.
XDP to najwcześniejszy możliwy hook – pakiet łapany na poziomie sterownika karty, zanim jądro w ogóle zbuduje dla niego swoje struktury. To daje ekstremalną wydajność i jest używane tam, gdzie liczy się przepustowość: filtrowanie DDoS, load balancing, wczesne odrzucanie. tc (traffic control) siedzi nieco wyżej, na wejściu i wyjściu np. karty sieciowej, i widzi już pakiet w kontekście stosu sieciowego. Te dwie warstwy razem pozwalają zdjąć pełny obraz konwersacji sieciowych – kto z kim, jakim protokołem, ile, jak długo – bez podpinania firewalla czy kolektora do kabla i bez mirroringu portu.
Przykładem może być Cilium i jego część Hubble. Różnica wobec klasycznego monitoringu jest tu dokładnie taka, jak opisałem wyżej: zamiast łapać pakiety na karcie i słać je do kolektora, Hubble obserwuje przepływy na poziomie eBPF w jądrze i generuje z nich gotowe, ustrukturyzowane zdarzenia – bez samplingu i bez narzutu pełnego „packet capture”. Ale najważniejsze jest to, co dokłada po drodze: kontekst. Klasyczny flow z drutu daje Ci piątkę (5-tuple) adres-port-adres-port-protokół i tyle – dalej musisz sam zgadywać, czyj to ruch. Hubble zna tożsamość, bo widzi zdarzenia wewnątrz hosta, zanim zostanie ono spłaszczone do samego IP: dokłada nazwy podów, namespace’y, etykiety, nazwy usług, a nawet werdykt polityki (przepuszczony / odrzucony) i zapytania DNS. W praktyce hubble observe pokazuje nie „10.0.3.14 gadał z 10.0.7.2″, tylko „usługa frontend w namespace prod odpytała usługę payments, polityka: dozwolone”. To jest ta sama telemetria flow co w poprzednim wpisie, ale wzbogacona o warstwę, do której nie ma dostępu z poziomu kabla sieciowego – bo na kablu tożsamość aplikacji została zastąpiona adresem IP.
W klastrze Kubernetes klasyczny model widoczności po prostu przestaje działać: ruch wschód-zachód między podami nie wychodzi na żaden fizyczny interfejs, który dałoby się zmirrorować, adresy IP są chwilowe i zmieniają się przy każdym restarcie poda, a jeden węzeł hostuje dziesiątki niezależnych usług. eBPF jest tam jedyną sensowną odpowiedzią, bo widzi ruch w punkcie, w którym tożsamość aplikacji istnieje i jest znana.
eBPF w rodzinie Wiresharka
Tu trzeba zacząć od wyjaśniania drobnego skrótu myślowego, bo „eBPF w Wiresharku” w potocznym rozumieniu łączy dwie rzeczy, które są spokrewnione, ale to nie to samo. Filtry capture w Wiresharku – te, które wpisujesz przed startem przechwytywania, jak tcp port 443 – od zawsze używają BPF, ale w jego oryginalnej, klasycznej odmianie: cBPF (classic BPF). To ten sam Berkeley Packet Filter, od którego wziął nazwę cały ekosystem, maleńka maszyna wirtualna w jądrze, która decyduje, które pakiety przepuścić do narzędzia, a które odrzucić już w kernelu, żeby nie przepisywać ich bez sensu do userspace. eBPF to rozszerzony następca tego właśnie mechanizmu – stąd litera „e”. Czyli związek jest genealogiczny: filtr tcp port 443, który klepiesz w Wiresharku od lat, to prapradziadek programu eBPF, który podpinaliśmy pod SSL_write w punkcie 3. Ta sama rodzina, inny poziom rozwoju.
Prawdziwa, współczesna integracja eBPF z Wiresharkiem idzie jednak inną drogą – przez extcap, czyli mechanizm wtyczek, który pozwala Wiresharkowi traktować dowolne zewnętrzne źródło jako „interfejs” do przechwytywania. I tu pojawia się cała klasa narzędzi, które to robią: biorą Twój filtr, kompilują go do bytecode’u eBPF, puszczają w jądrze i streamują dopasowane pakiety z powrotem do Wiresharka jako normalny strumień pcap-ng. Przykłady to gadżet tcpdump z Inspektor Gadget, Edgeshark – wszystkie pojawiają się w oknie interfejsów Wiresharka jako kolejne źródło. Różnica wobec klasycznego przechwytywania jest taka, że to źródło żyje w świecie kontenerów: łapie ruch wewnątrz klastra Kubernetes, z kontekstem podów i namespace’ów, bez konieczności znalezienia fizycznego interfejsu do mirrorowania. Dla sieciowca to jest ten moment, w którym znajome narzędzie nagle widzi ruch wschód-zachód, którego wcześniej nie dało się złapać – bo łapanie ruchu odbywa się w jądrze hosta.
Ale najciekawsze jest coś, co nie jest już nawet Wiresharkiem, tylko jego nowym bratem – Stratoshark. To wydane niedawno narzędzie od fundacji Wiresharka (i samego Geralda Combsa, twórcy Wiresharka), które robi dla wnętrza hosta to, co Wireshark robi dla sieci: zamiast pakietów analizujesz w nim wywołania systemowe i logi. Interfejs jest celowo bliźniaczy – ten sam silnik, ta sama logika filtrów, to samo okno – więc jeśli znasz Wiresharka, czujesz się od razu jak u siebie, tyle że patrzysz nie na tcp.flags, a na to, który proces otworzył jaki plik, nawiązał jakie połączenie, wykonał jaki syscall. A pod spodem Stratoshark zbiera dane przez biblioteki libscap i libsinsp z ekosystemu Falco – a te przechwytują zdarzenia przez moduł jądra albo, coraz częściej jako domyślny standard, sondę eBPF. Czyli ta sama mechanika, którą w punkcie 3 czytaliśmy bufor przed szyfrowaniem, a w punkcie 4 zdejmowaliśmy flow z kernela, tutaj zasila narzędzie analityczne wyglądające jak Wireshark, tylko skierowane do środka systemu zamiast na sieć.
Cisco Hypershield – eBPF jako produkt enterprise
Dotąd mówiliśmy o eBPF w rękach inżyniera: jednolinijkowiec w bpftrace, otwarte narzędzie jak ecapture, Cilium w klastrze. Hypershield to ten sam mechanizm ubrany w produkt enterprise i warto go opisać, bo pokazuje, dokąd ta technologia zmierza w świecie. Cisco pozycjonuje Hypershield jako rozproszoną architekturę bezpieczeństwa, a jej komponent działający na hoście nazywa się Tesseract Security Agent. I tu wszystko, co budowaliśmy przez poprzednie punkty, składa się w jedno: ten agent działa wewnątrz workloadu i rozmawia z jądrem systemu przez eBPF, obserwując procesy, dostępy do plików, połączenia sieciowe i wywołania systemowe – dokładnie te same punkty zaczepienia, o których była mowa przy fundamencie.
Co istotne, to nie jest własny, od zera napisany mechanizm – Hypershield stoi na Tetragonie, czyli komponencie z ekosystemu Cilium odpowiadającym za security observability i runtime enforcement. Tetragon trafił do Cisco wraz z przejęciem Isovalenta, firmy stojącej za Cilium. Ta sama otwarta technologia, która daje Ci hubble observe w klastrze, siedzi pod spodem enterprise’owego agenta bezpieczeństwa. To pokazuje, że granica między „narzędziem inżyniera” a „produktem za duże pieniądze” przebiega nie na poziomie mechanizmu, tylko na poziomie zarządzania, wsparcia i integracji.
Różnica wobec prostego przechwytywania z punktu 3 polega na dwóch rzeczach. Po pierwsze, Tesseract nie tylko patrzy – może też działać: gdy wykryje anomalie albo złośliwe zachowanie, wkracza i egzekwuje politykę, blokując albo alarmując, zależnie od konfiguracji. To jest ta część, której goła uprobe czytająca bufor nie ma: eBPF daje nie tylko widoczność w jądrze, ale i możliwość ingerencji w locie. Po drugie, z tej widoczności każdego wywołania procesu Hypershield buduje graf zachowania aplikacji i jej fingerprint – profil tego, co dana aplikacja normalnie robi: jakie pliki czyta, z kim się łączy, jakie syscalle wykonuje. Nowe zachowania są potem klasyfikowane względem tego profilu jako prawidłowe, podejrzane albo złośliwe.
Pamiętasz fingerprint aplikacji z poprzedniego odcinka – JA4 i Cisco EVE, rozpoznające proces kliencki po odcisku ClientHello z drutu, bez dekrypcji? Hypershield robi konceptualnie to samo – fingerprint aplikacji – ale z zupełnie innej strony. Tam odcisk powstawał z zewnątrz, z tego nielicznego, co wyciekało w handshake’u na kablu, i z natury był probabilistyczny: „to wygląda jak Firefox”. Tutaj fingerprint powstaje od środka hosta, z pełnego wglądu w to, co proces faktycznie robi na poziomie jądra. To jest dokładnie ten sam ruch, który opisuję od początku serii: skoro z drutu widać coraz mniej i coraz mniej pewnie, punkt obserwacji przesuwa się na endpoint, gdzie widać wszystko. EVE fingerprintuje aplikację po strzępach z sieci; Tesseract fingerprintuje ją po jej rzeczywistym zachowaniu w systemie. Ta sama idea, przeniesiona z drutu na host – i to przeniesienie jest możliwe właśnie dzięki eBPF.
Hypershield przetwarza telemetrię jak najbliżej źródła, budując grafy zachowań bezpośrednio na brzegu, zamiast słać surowe strumienie danych do backendu. To ta sama logika, co przy obserwowalności z punktu 4 – skoro zbierasz dane w jądrze każdego hosta, to i wstępną analizę opłaca się robić tam, a nie przepychać wszystko do centralnego kolektora.
eBPF pod maską całego rynku EDR/XDR
Zanim przejdziemy do podsumowania, trzeba postawić Hypershielda w szerszym kontekście, bo inaczej ty czytelniku wyjdziesz z wrażeniem, że Cisco wymyśliło coś nadzwyczajnego i wyjątkowego. Prawda jest dokładnie odwrotna: eBPF to dziś domyślny sposób, w jaki praktycznie każdy poważny EDR/XDR zbiera telemetrię na Linuksie. Hypershield jest tylko jednym, dobrze udokumentowanym przykładem technologii, która stała się standardem całej branży. I jest w tym pewna historyczna sprawiedliwość, o której warto wspomnieć: świat endpointów przez lata stał na modułach jądra ładowanych do systemu – a to dokładnie ten model, który w 2024 roku wywrócił globalny incydent CrowdStrike, gdy wadliwa aktualizacja sensora działającego na poziomie jądra Windows położyła miliony maszyn naraz. eBPF jest odpowiedzią na tę właśnie klasę ryzyka: daje wgląd w jądro bez wkładania do niego kruchego, uprzywilejowanego modułu, który może wywrócić cały system.
Krajobraz dzieli się na dwie warstwy. Pierwsza to komercyjne EDR/XDR, gdzie eBPF jest cichym fundamentem sensora na Linuksie. CrowdStrike Falcon oferuje na Linuksie sensor oparty o eBPF, dający pełną telemetrię z poziomu jądra bez ładowalnego modułu – i to jest u nich wskazywane jako przewaga w pokryciu Linuksa i workloadów kontenerowych. Microsoft Defender for Endpoint na Linuksie dostarczany jest jako sensor eBPF w postaci paczki (asymetrycznie wobec Windows, gdzie czujnik jest wbudowany w system, i macOS, gdzie używa się frameworka Apple). SentinelOne, Palo Alto Cortex XDR i reszta czołówki grają w tę samą grę na Linuksie. Wzorzec jest jednolity: na Windowsie własne mechanizmy systemowe, na macOS framework Apple (Endpoint Security), a na Linuksie – eBPF. Jeśli więc masz w firmie którykolwiek z tych produktów na serwerach Linux, to z dużym prawdopodobieństwem już dziś masz na nich agenta eBPF, czytającego syscalle i połączenia w jądrze, tylko pod inną nazwą handlową.
Druga warstwa to otwarte narzędzia runtime security, które warto znać, bo to z nich wyrosły komercyjne produkty. Trzy nazwy do zapamiętania: Falco (host-based IDS dla syscalli, ten sam, który zasila Stratosharka z punktu 5 – detekcja, bez blokowania), Tetragon (ten sam, na którym stoi Hypershield – jako jedyny z otwartej trójki ma natywny enforcement w jądrze przez hooki LSM, czyli potrafi nie tylko wykryć, ale i zablokować albo ubić proces) oraz Tracee od Aqua Security. To jest ten sam wzorzec, który widzieliśmy przy fingerprintingu w poprzednim wpisie: EVE Cisco był udokumentowanym przykładem podejścia, które w tej czy innej formie ma każdy vendor NGFW. Tutaj Hypershield jest udokumentowanym przykładem podejścia, które na Linuksie ma każdy vendor EDR/XDR – a pod spodem jednego i drugiego siedzi dokładnie ta sama otwarta technologia.
To wszystko kończy się na Linuksie. eBPF jest funkcją jądra Linuksa – Falco, Tetragon, Tracee i eBPF-owe sensory komercyjnych EDR-ów działają na hostach i kontenerach linuksowych. Na Windowsie te produkty realizują swoją funkcję zupełnie inaczej (stąd osobne czujniki), a sam Windows ma własny, dopiero dojrzewający port eBPF, który to inna historia. Dla sieciowca wniosek jest praktyczny: cała ta rewolucja widoczności na hoście, którą opisuję w tym odcinku, dotyczy przede wszystkim świata serwerów, chmury i kontenerów – czyli dokładnie tam, gdzie i tak przeniósł się ciężar ruchu.
Podsumowanie
Zrób krok wstecz i spójrz na całą trylogię z lotu ptaka. Zaczęła się od tego, że sieć ślepnie: TLS 1.3, ECH, QUIC i pinning krok po kroku zabierały to, co pasywny obserwator mógł wyczytać z drutu, aż został sam fingerprint i kształt flow – prawdopodobieństwo zamiast pewności. Drugi odcinek był o życiu w tej ślepocie: jak z metadanych i odcisków wyciągnąć tyle, ile się da, wiedząc, że to triage, nie dowód. A ten, trzeci, jest o tym, dokąd uciekła pewność – i odpowiedź brzmi: na host. eBPF to mechanizm, który tam sięga, i dzięki niemu treść, którą sieć straciła, znów jest w zasięgu: jawny tekst przed szyfrowaniem SSL_write, flow uzyskiwane bezpośrednio z jądra, syscalle, zachowanie aplikacji. Widoczność wróciła. Ale wróciła w inne miejsce, i to „inne miejsce” zmienia wszystko.
Bo to nie jest ten sam rodzaj widoczności, który straciliśmy. Stary model był pasywny i bezkontaktowy: tap na kablu, SPAN port, kolektor z boku, dekrypcja szyfrowanego ruchu. Aplikacja nie wiedziała, że jest obserwowana, nie trzeba było niczego instalować na hoście, a punkt obserwacji był fizycznie oddzielony od tego, co obserwował. Nowy model jest dokładnie odwrotny. Wymaga agenta na każdym hoście, działającego z uprawnieniami pozwalającymi czytać pamięć cudzych procesów i wpinać kod w jądro. To nie jest już neutralny punkt z boku – to uprzywilejowana obecność w środku systemu, który chcesz chronić. Dla człowieka od sieci to zmiana tożsamości zawodowej: punkt kontroli przestał być na firewallu, który był Twoją domeną, i przeniósł się na endpoint, który był domeną kogoś innego.
I trzeba uczciwie dopowiedzieć, co się z tym modelem wiąże, bo eBPF bywa sprzedawany jak magia, a magii nie ma. Po pierwsze, to Linux – cała ta rewolucja dotyczy serwerów, chmury i kontenerów, a nie całego świata (Windows i macOS idą inną drogą). Po drugie, jest zależny od jądra: wersje, BTF, CO-RE, uprawnienia – to nie jest „włącz i działa” na dowolnej maszynie. Po trzecie, ma koszt – hooki kosztują cykle, a pełne przechwytywanie treści nie jest darmowe. I po czwarte przesuwa zaufanie i powierzchnię ataku do jądra. Ten sam uprzywilejowany dostęp, który daje Ci widoczność, w rękach atakującego z rootem jest narzędziem do wykradania sekretów z pamięci – ta sama uprobe na SSL_write, której Ty używasz do obrony, jest idealnym narzędziem ofensywnym. Verifier chroni jądro przed błędnym programem, ale nie zmienia faktu, że dałeś komuś (albo czemuś) prawo patrzenia w środek wszystkiego.
