Przejdź do treści
INDEKS ANALIZ BREACHROAD / NOTA TECHNICZNA

Container escape: namespaces, seccomp i AppArmor w praktyce

Techniczny model container escape bez exploitów: Linux namespaces, capabilities, seccomp, AppArmor, OCI, detekcja runtime i hardening hosta.

MATERIAŁ PUBLICZNY
AUTOR
/ CEO Breachroad · OSCP · PNPT
PUBLIKACJA
21 kwietnia 2026
CZAS CZYTANIA
22 min czytania
TEMAT
Chmura, infrastruktura i DevSecOps
Container escape: namespaces, seccomp i AppArmor w praktyce

Container escape oznacza przekroczenie granicy izolacji kontenera i uzyskanie niezamierzonego wpływu na host, runtime albo inne workloady. Nie jest nazwą jednej techniki. Może wynikać z podatności jądra lub runtime, ale równie często źródłem jest konfiguracja: tryb privileged, host namespaces, niebezpieczne capabilities, zamontowany socket runtime, urządzenia hosta albo zapisywalny wolumen z wrażliwą ścieżką.

Najważniejsza odpowiedź brzmi: kontener korzysta ze wspólnego jądra hosta, więc jego bezpieczeństwo zależy od kilku nakładających się kontroli. Namespaces ograniczają widoczność, cgroups zasoby, capabilities dzielą uprawnienia roota, seccomp filtruje syscalls, a AppArmor lub SELinux ogranicza operacje procesu. Obraz, runtime, kernel i orkiestrator muszą być aktualne. Wyłączenie jednej warstwy nie zawsze prowadzi do ucieczki, lecz zmniejsza margines bezpieczeństwa i wzmacnia kolejne błędy.

Container escape, breakout i zwykłe przejęcie aplikacji

Trzeba rozróżnić trzy skutki. Przejęcie procesu w kontenerze daje kod z jego UID, systemem plików, tokenami i siecią, ale nie dowodzi dostępu do hosta. Przekroczenie granicy workloadu może oznaczać odczyt współdzielonego wolumenu albo użycie tokenu do ataku na inne zasoby. Dopiero container escape daje niezamierzone wykonanie lub kontrolę w przestrzeni hosta bądź runtime.

Ta klasyfikacja chroni przed dwoma błędami raportowymi. Nie należy nazywać każdej powłoki w kontenerze „przejęciem węzła”. Z drugiej strony RCE aplikacji nie jest niskim ryzykiem tylko dlatego, że działa w kontenerze: proces może posiadać chmurową tożsamość, sekrety i sieciową ścieżkę do krytycznych usług.

Warstwowy model izolacji Linuksa

WarstwaCo ograniczaCzego nie gwarantuje
Namespaceswidok procesów, mountów, sieci, IPC, hostname, użytkownikówodporności na podatność wspólnego jądra
cgroupsCPU, pamięć, I/O, PID i rozliczanie zasobówkontroli dostępu do danych i syscalls
capabilitiesdzieli tradycyjne prawa roota na mniejsze zdolnościbezpieczeństwa przy zbyt szerokim zestawie lub błędzie kernela
seccompdopuszcza lub blokuje wywołania systemowe i wybrane argumentypełnego modelu dostępu do plików i obiektów
AppArmor/SELinuxobowiązkowa polityka dostępu procesupoprawności samej aplikacji i wszystkich reguł
user namespace/rootlessmapuje UID i ogranicza znaczenie roota konteneraeliminacji wspólnego kernela i błędów konfiguracji
OCI runtimetworzy proces, namespaces, mounty, urządzenia i politykibezpieczeństwa przy starej wersji lub niebezpiecznym configu

Bezpieczeństwo wynika z kompozycji. Najlepszy test nie szuka jednego „magicznego” ustawienia, lecz sprawdza, czy kilka niezależnych kontroli ogranicza ten sam niepożądany skutek.

Linux namespaces: izolacja widoku, nie osobne jądro

Namespaces sprawiają, że proces widzi kontrolowaną część zasobów systemowych. Najważniejsze typy to:

  • PID — oddzielne drzewo procesów;
  • mount — odrębny widok punktów montowania;
  • network — interfejsy, adresy, routing, porty i część stosu sieciowego;
  • IPC — wybrane mechanizmy komunikacji międzyprocesowej;
  • UTS — hostname i nazwa domeny;
  • user — mapowanie UID/GID i capabilities w zakresie namespace;
  • cgroup — widok hierarchii cgroups;
  • time — wybrane zegary namespace.

Współdzielenie hostPID, hostNetwork albo hostIPC usuwa część separacji. Nie zawsze jest podatnością — agenty bezpieczeństwa i sieci mogą potrzebować takiego dostępu — ale wymaga wyższego zaufania, osobnego ServiceAccount i ścisłej kontroli obrazu. Mount namespace również traci znaczenie, jeśli workload dostaje zapisywalny dostęp do krytycznej ścieżki hosta.

User namespaces redukują wpływ UID 0 w kontenerze, mapując go na nieuprzywilejowany identyfikator hosta. Nie są jednak zamiennikiem seccomp, polityki MAC i aktualizacji kernela. Rootless Docker dodatkowo uruchamia daemon i kontenery bez roota, ograniczając skutki przejęcia runtime, ale ma wymagania i ograniczenia funkcjonalne opisane w dokumentacji rootless mode.

Linux capabilities i problem „prawie privileged”

Capabilities dzielą monolityczne uprawnienia roota. Runtime zwykle zachowuje ograniczony zestaw, a resztę usuwa. Ryzyko rośnie, gdy konfiguracja dodaje zdolności niezwiązane z funkcją aplikacji, szczególnie szerokie możliwości administracji systemem, modułów, urządzeń, sieci i śledzenia procesów.

Nie wystarczy przegląd pola cap_add. Trzeba sprawdzić permitted, effective, inheritable, bounding i ambient sets procesu oraz flagę no_new_privileges. Proces potomny może zmienić efektywny zestaw w zależności od pliku wykonywalnego, UID i reguł securebits. Bezpieczny baseline zaczyna od drop: ALL i dodaje tylko zdolność potwierdzoną testem funkcjonalnym.

Tryb privileged jest kategorią osobną. Runtime nadaje bardzo szerokie prawa, dostęp do urządzeń i łagodniejsze profile bezpieczeństwa. Nie powinien być skrótem naprawiającym błędy aplikacji. Jeżeli komponent rzeczywiście wymaga wysokich uprawnień, warto odseparować go na dedykowanych węzłach i ograniczyć jego kod, sieć oraz tożsamość.

Seccomp: redukcja powierzchni syscalls

Seccomp filter pozwala procesowi załadować filtr BPF, który ocenia numer syscalla, architekturę i dostępne argumenty. Filtr może zezwolić, zwrócić błąd, zakończyć proces, wygenerować pułapkę lub zastosować inne obsługiwane działanie. Seccomp nie jest sandboxem samym w sobie; dokumentacja kernela wprost wskazuje, że powinien współpracować z innymi mechanizmami.

Domyślny profil Dockera blokuje wybrane syscalls, zachowując model allowlisty odpowiedni dla typowych kontenerów. Dokumentacja seccomp Dockera zaleca pozostawienie profilu domyślnego, jeśli aplikacja nie ma uzasadnionej potrzeby zmiany. seccomp=unconfined poszerza powierzchnię kernela i powinno być wyjątkiem.

Test profilu powinien odpowiadać na trzy pytania:

  1. czy profil jest rzeczywiście załadowany dla działającego procesu;
  2. które syscalls aplikacja wykorzystuje w normalnym i awaryjnym przebiegu;
  3. czy zablokowanie niepotrzebnej klasy wywołań generuje obserwowalne zdarzenie bez przerywania usługi.

Profil tworzony wyłącznie z krótkiego trace może być zbyt wąski dla rzadkiej ścieżki, aktualizacji biblioteki lub obsługi sygnału. Należy testować go regresyjnie w CI i na canary deployment, a nie uczyć bezpośrednio na produkcji.

AppArmor: polityka dostępu oparta na ścieżkach

AppArmor przypisuje profil do programu i ogranicza pliki, capabilities, sieć, sygnały, mounty oraz inne operacje wspierane przez politykę. Tryb complain loguje naruszenia, ale ich nie egzekwuje; tryb enforce blokuje. Kontener bez właściwie załadowanego profilu może działać inaczej niż wskazuje manifest.

Docker AppArmor documentation opisuje domyślny profil docker-default, generowany i ładowany dla kontenerów. W orkiestratorze trzeba zweryfikować profil na każdym węźle, nazwę wskazaną workloadowi i zachowanie po aktualizacji. Polityka powinna chronić szczególnie ścieżki kernela, runtime, poświadczenia hosta i niepotrzebne narzędzia administracyjne.

AppArmor jest path-based, więc aliasy ścieżek, bind mounty i zmienny layout aplikacji wymagają testów. Nie należy kopiować profilu z innej dystrybucji bez sprawdzenia nazw plików i bibliotek.

OCI runtime i niebezpieczne punkty konfiguracji

OCI Runtime Specification standaryzuje m.in. namespaces, UID/GID mappings, urządzenia, resources, seccomp, masked paths i read-only paths. Implementacja runtime tworzy proces kontenera na podstawie bundle i konfiguracji.

W audycie sprawdzamy:

  • wersję oraz poprawki runc, containerd, CRI-O i kernela;
  • dostęp do socketów Docker, containerd, CRI lub kubelet;
  • privileged, host namespaces i hostPath;
  • urządzenia, device cgroup i szerokie capabilities;
  • writable /proc, /sys, cgroupfs i ścieżki runtime;
  • maskedPaths, readonlyPaths, read-only root filesystem i mount propagation;
  • no_new_privileges, seccomp oraz profil AppArmor/SELinux;
  • możliwość zmiany specyfikacji przez admission webhook, operator lub pipeline.

Socket runtime jest interfejsem zarządzającym, nie zwykłym plikiem. Workload mogący tworzyć uprzywilejowane kontenery przez daemon w praktyce przekracza zamierzoną granicę, nawet bez exploita kernela.

Najczęstsze ścieżki do wpływu na host

Ścieżki można pogrupować w cztery kategorie:

  1. błąd oprogramowania — podatność kernela, runtime, filesystemu lub komponentu orkiestracji;
  2. nadmierna konfiguracja — privileged, host namespaces, capabilities, urządzenia i host mounts;
  3. interfejs zarządzający — socket runtime, kubelet, API klastra lub panel węzła;
  4. łańcuch dostaw — złośliwy lub podmieniony obraz uruchomiony w zaufanym kontekście.

Ostatnia kategoria łączy runtime z bezpieczeństwem obrazów kontenerów i SBOM w formatach CycloneDX, SPDX oraz VEX. Podpis obrazu nie ogranicza jego capabilities, a seccomp nie potwierdza pochodzenia kodu — kontrole rozwiązują różne problemy.

Bezpieczna metodologia testu izolacji

1. Najpierw analiza konfiguracji

Zbieramy OCI spec, manifest workloadu, polityki admission, runtime, kernel, profile seccomp/MAC, mounty, devices i tożsamość. Porównujemy stan deklarowany z działającym procesem. Sama konfiguracja Helm nie dowodzi, że wszystkie repliki korzystają z tego samego profilu.

2. Laboratorium zgodne z produkcją

Testy zachowania kernela i runtime wykonujemy na odtworzonym węźle o tej samej wersji, konfiguracji i sterownikach. Nie uruchamiamy publicznych exploitów kernela na produkcji. Jeżeli CVE ma znaczenie, dowodem może być podatna wersja plus osiągalne warunki, a dynamiczną demonstrację ograniczamy do disposable lab.

3. Harmless canaries

Przygotowujemy niedostępne dla kontenera pliki-canary, zabronione połączenie i syscall testowy bez skutku biznesowego. Potwierdzamy odmowę oraz log seccomp/AppArmor. Nie próbujemy modyfikować hosta, ładować modułu ani uzyskiwać trwałości.

4. Test regresyjny

Po zaostrzeniu profilu uruchamiamy normalne ścieżki, start, shutdown, health checks, backup, diagnostykę i obsługę awarii. Kontrola, która łamie aktualizację lub reakcję na incydent, zostanie szybko wyłączona.

5. Retest na działającym workloadzie

Weryfikujemy efektywny UID, capabilities, NoNewPrivs, status seccomp, profil MAC, mounty i namespaces każdej klasy workloadu. Sprawdzamy również węzły zapasowe i pule autoscaling.

Detekcja prób container escape

Telemetria powinna łączyć zdarzenia procesu, kernela, runtime, Kubernetes i chmury. Sygnały wysokiej wartości obejmują:

  • AppArmor DENIED, zdarzenia seccomp i odmowy LSM;
  • nietypowe syscalls, namespace creation lub dostęp do /proc i /sys;
  • uruchomienie narzędzi administracyjnych, kompilatora lub debuggera w obrazie runtime;
  • dostęp do socketu runtime, kubeletu i metadanych chmurowych;
  • zmianę capabilities, mountów, urządzeń albo trybu privileged;
  • proces w kontenerze inicjujący połączenia do paneli węzła;
  • rozbieżność między obrazem, podpisem i uruchomionym procesem;
  • nowy Pod z host namespaces lub hostPath poza zatwierdzonym DaemonSetem.

Logi muszą opuścić węzeł, ponieważ po rzeczywistym escape lokalny napastnik może próbować je zmienić. Alert powinien zachować identyfikator kontenera, image digest, Pod, namespace, ServiceAccount, node i proces nadrzędny.

Hardening hosta i workloadu

  1. Aktualizuj kernel, runtime i orkiestrator według ryzyka oraz ekspozycji.
  2. Uruchamiaj proces jako nie-root i rozważ user namespaces lub rootless.
  3. Ustaw no-new-privileges, drop ALL i dodawaj minimalne capabilities.
  4. Zachowaj domyślny seccomp albo bardziej restrykcyjny profil przetestowany regresyjnie.
  5. Egzekwuj AppArmor/SELinux zamiast trybu complain/unconfined.
  6. Używaj read-only root filesystem i minimalnych zapisywalnych wolumenów.
  7. Blokuj privileged, host namespaces, hostPath, niezatwierdzone urządzenia i sockety runtime.
  8. Separuj wysoko uprzywilejowane DaemonSety na dedykowane węzły.
  9. Minimalizuj obraz i stosuj hardening Linuksa na hoście.
  10. Projektuj domyślne bezpieczne ustawienia zgodnie z Secure by Design.

W orkiestracji te ustawienia powinny być egzekwowane jako kod i sprawdzane razem z szerszym programem bezpieczeństwa Kubernetes i kontenerów, a nie pozostawione do decyzji autora pojedynczego manifestu.

Checklista odporności na container escape

  • Każdy workload ma uzasadniony UID/GID i nie działa domyślnie jako root.
  • privileged, host namespaces, hostPath i urządzenia są zabronione domyślnie.
  • Capabilities zaczynają od drop ALL i mają właściciela wyjątku.
  • Seccomp jest załadowany i zweryfikowany na działającym procesie.
  • AppArmor/SELinux działa w trybie enforce na każdym węźle.
  • Sockety runtime i kubelet nie są montowane do zwykłych workloadów.
  • Kernel i runtime mają inwentaryzację wersji oraz SLA aktualizacji.
  • Testy izolacji odbywają się na zgodnym laboratorium, nie przez exploit produkcji.
  • SOC koreluje kernel, runtime, Pod, ServiceAccount i image digest.
  • Retest obejmuje autoscaling, węzły zapasowe i ścieżki awaryjne aplikacji.

Źródła pierwotne

UDOSTĘPNIJ / KOPIUJ