Artykuł pochodzi z wydania: Kwiecień 2026
Kontenery stały się fundamentem współczesnej infrastruktury aplikacji, pozwalając przenosić środowiska z jednej maszyny na drugą niemal bez zmian. To nie oznacza jednak, że same w sobie zapewniają ochronę – Docker nie jest mechanizmem bezpieczeństwa. To platforma służąca do izolacji procesów oparta na funkcjach jądra Linux. Jeśli izolacja jest traktowana jako granica zaufania, trzeba ją świadomie wzmocnić.
Bezpieczeństwo kontenerów nie zaczyna się w momencie uruchomienia procesu w Dockerze, lecz od architektury izolacji i sposobu, w jaki kontenery współdziałają z hostem. Mechanizmy takie jak namespaces, cgroups, capabilities, seccomp czy profile AppArmor i SELinux definiują granice izolacji, ale ich skuteczność zależy od świadomej konfiguracji i zrozumienia ograniczeń. Root w kontenerze nie jest rootem na hoście, ale przy nieprawidłowym użyciu opcji takich jak –privileged może prowadzić do pełnego obejścia izolacji. Montowanie socketu demona Docker czy brak deterministycznych buildów i podpisywania obrazów w pipelinie zwiększa powierzchnię ataku w sposób, który nie jest od razu widoczny na poziomie runtime’u.
W pierwszej części tematu numeru przeanalizujemy praktyczne aspekty bezpieczeństwa Docker Engine’u, pokazując, które mechanizmy działają domyślnie, a które wymagają świadomej konfiguracji, oraz jak typowe błędy w produkcyjnych wdrożeniach mogą się przekładać na eskalację ryzyka. Pokażemy, w jaki sposób poszczególne mechanizmy izolacji współgrają ze sobą, gdzie pojawiają się typowe błędy operacyjne i jakie konsekwencje architektoniczne wynikają z decyzji podejmowanych podczas wdrożeń produkcyjnych. Analiza obejmuje zarówno ograniczenia techniczne, jak i realne implikacje dla operacyjności środowiska – w tym, które działania w teorii zwiększają bezpieczeństwo, a w praktyce mogą wprowadzać punkty awarii lub nowe wektory ataku.
> MODEL ZAGROŻEŃ DLA DOCKERA
Dyskusja o utwardzaniu środowiska kontenerowego ma sens dopiero wtedy, gdy rozdzielimy cztery jakościowo różne klasy kompromitacji. W praktyce są one często wrzucane do jednego worka pod hasłem „ucieczka z kontenera”, co zaciemnia analizę i prowadzi do błędnych decyzji architektonicznych.
Pierwsza sytuacja to kompromitacja aplikacji działającej w kontenerze. Mówimy tu o podatnościach w kodzie – RCE (remote code execution), deserializacji, błędach autoryzacji – które pozwalają atakującemu wykonywać polecenia w kontekście procesu wewnątrz kontenera. Z perspektywy jądra jest to po prostu proces uruchomiony w określonym zestawie przestrzeni nazw i z przypisanym zestawem capabilities. Jeżeli kontener działa jako root i nie ograniczono mu przywilejów, atakujący uzyskuje dokładnie ten kontekst. Nie jest to jeszcze kompromitacja hosta, ale może się nią stać, jeśli dalsze warunki na to pozwolą.
Druga kategoria to kompromitacja runtime’u, czyli samego demona kontenerowego. W przypadku środowiska opartego na Docker Enginie oznacza to przejęcie kontroli nad procesem dockerd lub jego API. Demon działa z uprawnieniami roota i jest pośrednikiem pomiędzy użytkownikiem a jądrem. Jeżeli atakujący uzyska możliwość wydawania poleceń przez API – bez względu na to, czy poprzez nieautoryzowany dostęp do socketu, czy za pomocą podatności w komponencie pośrednim – może tworzyć kontenery z dowolnymi parametrami, w tym z montowaniem systemu plików hosta czy uruchamianiem w trybie uprzywilejowanym. To nie jest „ucieczka z kontenera”. To przejęcie mechanizmu, który kontenery tworzy.
Trzecia warstwa to kompromitacja hosta. Tutaj mówimy o scenariuszu, w którym atakującemu udaje się wykonać kod poza przestrzeniami nazw kontenera – w kontekście systemu operacyjnego jako całości. Może to wynikać z podatności w jądrze, z nadmiarowych capabilities przyznanych kontenerowi, z trybu –privileged, z użycia –pid=host lub –net=host, albo z montowania wrażliwych ścieżek systemowych. To moment, w którym izolacja nie działa już jako bariera, a środowisko kontenerowe przestaje mieć znaczenie – atakujący operuje na poziomie hosta.
Czwarta kategoria, często niedoceniana, to kompromitacja pipeline’u budowania obrazów. Jeżeli atakujący wprowadzi złośliwy kod do obrazu na etapie builda, manipulując bazowym obrazem, zależnościami lub skryptami w Dockerfile, kompromitacja zostanie powielona na każdym hoście, który ten obraz uruchomi. W tym modelu runtime może być skonfigurowany poprawnie, a mimo to uruchamia kod, który od początku był skażony. Granica zaufania przebiega wtedy nie na hoście, lecz w łańcuchu dostaw.
Rozróżnienie tych czterech scenariuszy pozwala zrozumieć, że bezpieczeństwo Dockera jest złożonym zagadnieniem. Każdy z nich wymaga innego zestawu kontroli i innego sposobu myślenia o izolacji.
Separacja kontenera nie jest konstruktem abstrakcyjnym. Opiera się na konkretnych mechanizmach jądra Linux. Przestrzenie nazw, czyli namespaces, oddzielają widoczność zasobów. Instancja w odrębnej przestrzeni PID widzi tylko swoje procesy. W izolowanej przestrzeni sieciowej ma własny stos TCP/IP. W mount operuje na własnym widoku systemu plików. Docker tworzy zestaw takich przestrzeni przy uruchomieniu kontenera, dzięki czemu proces w środku ma wrażenie działania w odrębnym systemie. W praktyce jest to filtr percepcji, nie pełna izolacja wykonawcza. Proces nadal korzysta z tego samego jądra, co host.
Mechanizm cgroups odpowiada za kontrolę zasobów – CPU, pamięci, I/O. Nie jest to funkcja bezpieczeństwa w sensie kontroli dostępu, lecz ograniczania zużycia. Jednak w modelu zagrożeń ma znaczenie – bez limitów pamięci proces przejęty przez atakującego może doprowadzić do wyczerpania zasobów i odmowy usługi na poziomie hosta. Cgroups nie zapobiegają eskalacji przywilejów, ale ograniczają skutki nadużyć zasobowych.
Capabilities to precyzyjniejszy model przywilejów niż klasyczne rozróżnienie root–non-root. W systemie Linux root nie jest bytem binarnym – jego uprawnienia można rozłożyć na kilkadziesiąt niezależnych możliwości, takich jak modyfikacja konfiguracji sieci czy ładowanie modułów jądra. Docker domyślnie uruchamia kontener jako root wewnątrz przestrzeni nazw, ale z niepełnym zestawem capabilities. To ograniczenie nie jest jednak całkowite – część przywilejów pozostaje dostępna, a ich zestaw zależy od wersji i konfiguracji. Jeżeli kontenerowi przyznamy –privileged, mechanizm ten przestaje mieć znaczenie – kontener otrzymuje praktycznie wszystkie uprawnienia hosta.
Seccomp jest filtrem wywołań systemowych. Umożliwia ograniczenie zestawu syscalli, które proces może wykonać. Docker domyślnie stosuje profil seccomp, który blokuje część niebezpiecznych wywołań, takich jak manipulacja modułami jądra. Ten profil jest kompromisem między bezpieczeństwem a kompatybilnością. Nie jest maksymalnie restrykcyjny, ponieważ wiele aplikacji przestałoby działać. W efekcie seccomp redukuje powierzchnię ataku, ale nie eliminuje możliwości wykorzystania podatności w jądrze, jeśli dany syscall pozostaje dozwolony.
[…]
Adam Kamiński
Autor jest operatorem SOC-a i szkoleniowcem w zakresie cyberbezpieczeństwa.





