containerd po cichu łata krytyczny wektor ataku na węzły Kubernetes

18 czerwca 2026 r. projekt containerd opublikował wydania 2.3.2, 2.2.5 i 2.1.9 z poprawkami bezpieczeństwa dla kilku podatności w runtime kontenerów. Dla administratorów Linuksa najważniejszy jest tu nie sam fakt kolejnego patch release, lecz charakter błędów: dotyczą one warstwy CRI używanej przez Kubernetes, lokalnego cache obrazów na węźle oraz mechanizmów checkpoint/restore. 25 czerwca 2026 r. Canonical dołączył do tego własny komunikat USN-8473-1 dla Ubuntu 26.04 LTS i Ubuntu 25.10, a 1 lipca 2026 r. NVD opublikował analizę CVE-2026-50195 z oceną CVSS 3.1 na poziomie 9.9.

Schemat pokazujący zatrucie lokalnego cache obrazów containerd na węźle Kubernetes
CVE-2026-50195 dotyczy lokalnego cache obrazów i polityk imagePullPolicy w Kubernetes.

To jest temat praktyczny, bo containerd rzadko trafia na pierwszą stronę dashboardu, a jednocześnie jest jednym z najważniejszych procesów na linuksowym węźle klastra. Jeżeli kubelet rozmawia z runtime przez CRI, runtime odpowiada za pobieranie obrazów, uruchamianie kontenerów, snapshoty, shim-y i sporą część tego, co w operacyjnym skrócie nazywamy „działa mi pod”. W przypadku tej serii poprawek problem nie sprowadza się do abstrakcyjnego CVE w bibliotece: część wektorów ataku dotyka dokładnie tych miejsc, które administratorzy często zostawiają poza codziennym hardeningiem.

Co dokładnie zostało wydane 18 czerwca 2026 r.

Release containerd 2.3.2 jest drugim wydaniem poprawkowym gałęzi 2.3 i zawiera poprawki bezpieczeństwa dla CVE-2026-50195, CVE-2026-53488, CVE-2026-53492, CVE-2026-53489 oraz CVE-2026-47262. Równolegle projekt udostępnił poprawione wydania dla starszych wspieranych gałęzi: 2.2.5 oraz 2.1.9. To ważne, ponieważ w realnych środowiskach Kubernetes wersja runtime jest często powiązana z obrazem węzła, dystrybucją albo profilem managed Kubernetes, a nie z ręcznie instalowaną paczką z GitHuba.

Najgłośniejsza podatność z tej paczki to CVE-2026-50195, opisana jako możliwość zatrucia lokalnego tagu obrazu podczas importu checkpointu CRI. Dotknięte wersje to containerd od 2.1.0 do poniżej 2.1.9, od 2.2.0 do poniżej 2.2.5 oraz od 2.3.0 do poniżej 2.3.2. Poprawki są więc jasne: co najmniej 2.1.9, 2.2.5 albo 2.3.2, zależnie od używanej gałęzi.

Canonical w USN-8473-1 wskazał dla Ubuntu 26.04 LTS pakiet containerd-stable 2.2.2-0ubuntu1.1, a dla Ubuntu 25.10 pakiet 2.1.6-0ubuntu1~25.10.2. To dobry przykład, że numer wersji pakietu dystrybucyjnego nie zawsze musi wyglądać tak samo jak numer wydania upstream. W praktyce trzeba czytać advisory dystrybucji, a nie wyłącznie porównywać gołym okiem wersję z komunikatem projektu.

Dlaczego CVE-2026-50195 jest szczególnie niewygodne

Techniczna lista kroków aktualizacji containerd na linuksowym węźle Kubernetes
Aktualizacja runtime powinna być połączona z cordon, drain i kontrolą wersji na węzłach.

CVE-2026-50195 dotyczy procesu importowania checkpointu przez CRI. Według opisu podatności atakujący z uprawnieniami do tworzenia podów może przygotować złośliwy obraz checkpointu tak, aby containerd pobrał wskazany obraz i przypisał mu arbitralny lokalny tag. Problem zaczyna się wtedy, gdy inny pod na tym samym węźle uruchamia obraz z polityką IfNotPresent albo Never. Kubernetes w takich ustawieniach korzysta odpowiednio preferencyjnie albo wyłącznie z lokalnie obecnego obrazu. Jeżeli lokalny cache został zatruty, ofiara może uruchomić nie ten obraz, który zakładał manifest.

To nie jest klasyczny scenariusz „root na hoście po jednym requestcie HTTP”. Warunkiem jest możliwość tworzenia podów i wykorzystanie określonego zachowania CRI oraz cache obrazów. Jednak w środowiskach multi-tenant, w klastrach CI/CD, w platformach developerskich i w namespace’ach, gdzie zespoły mają dużą swobodę tworzenia workloadów, taki warunek wcale nie jest egzotyczny. Wystarczy, że atakujący kontroluje manifest poda w jednym obszarze klastra i potrafi wpłynąć na stan konkretnego węzła.

Najbardziej ryzykowne są środowiska, w których łączą się trzy praktyki: dopuszczanie nie w pełni zaufanych użytkowników do tworzenia podów, używanie tagów bez digestów oraz szerokie stosowanie imagePullPolicy: IfNotPresent. Ten ostatni wybór jest popularny, bo zmniejsza liczbę pobrań z registry i przyspiesza restarty. Po CVE-2026-50195 widać jednak, że cache na węźle jest elementem granicy zaufania, a nie neutralnym przyspieszaczem.

Nie tylko jeden CVE: DoS, host access i checkpoint restore

W tej samej serii poprawek znalazły się też inne podatności, które zasługują na uwagę administratora. CVE-2026-47262 dotyczy nadmiernego zużycia pamięci przez błędne parsowanie grup podczas tworzenia kontenerów z obrazów. CVE-2026-53489 wiąże się z walidacją ścieżek symbolicznych przy odtwarzaniu checkpointów i może prowadzić do odczytu plików z hosta. CVE-2026-53492 dotyczy zaufania do adnotacji interfejsów urządzeń podczas restore checkpointów, co może pozwolić na obejście ograniczeń zasobów i wstrzyknięcie urządzeń lub mountów hosta do kontenera. CVE-2026-53488 opisano jako problem z propagacją zastrzeżonych etykiet z konfiguracji obrazu do kontenerów, potencjalnie prowadzący do wykonania kodu na hoście.

Wspólny mianownik jest ciekawy: nie są to błędy w aplikacji działającej w kontenerze, ale w tym, jak runtime interpretuje metadane, etykiety, checkpointy, ścieżki i obrazy. Dla zespołów DevOps oznacza to, że skanowanie wyłącznie warstw aplikacyjnych nie wystarczy. Trzeba osobno pilnować wersji runtime, konfiguracji CRI, polityk admission oraz ustawień pobierania obrazów.

Jak sprawdzić, czy klaster używa podatnego runtime

Pierwszy krok to inwentaryzacja. W Kubernetes najprościej sprawdzić wersję runtime raportowaną przez kubelet. Poniższe polecenia nie naprawiają problemu, ale szybko pokażą, które węzły wymagają dalszej weryfikacji:

kubectl get nodes -o wide
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.containerRuntimeVersion}{"\n"}{end}'

# Na samym węźle Linux:
containerd --version || true
ctr version || true
systemctl status containerd --no-pager

Jeżeli wynik zawiera containerd 2.1.x poniżej 2.1.9, 2.2.x poniżej 2.2.5 albo 2.3.x poniżej 2.3.2, trzeba zaplanować aktualizację. W klastrach zarządzanych przez dostawcę chmury nie należy podmieniać binarki ręcznie, bo wersja runtime jest zwykle częścią obrazu node poola. W takim przypadku sensowną ścieżką jest aktualizacja obrazu węzła, rotacja node poola albo wymuszenie rebuildów maszyn zgodnie z dokumentacją dostawcy.

W klastrach własnych, opartych o pakiety dystrybucyjne, procedura powinna być bardziej klasyczna: aktualizacja paczki, restart containerd i kontrolowany drain węzłów. Nie warto restartować runtime pod obciążeniem bez planu, bo może to przerwać działające workloady zależnie od konfiguracji kubeleta, CRI i nadzorowania podów.

Przykładowa procedura dla węzłów Ubuntu

Na Ubuntu objętym komunikatem USN-8473-1 Canonical wskazuje, że po standardowej aktualizacji systemu należy zrestartować containerd. W praktycznym środowisku Kubernetes lepiej połączyć to z drainem węzła, aby uniknąć przypadkowego wpływu na workloady produkcyjne:

# Z maszyny administracyjnej:
kubectl cordon worker-01
kubectl drain worker-01 --ignore-daemonsets --delete-emptydir-data

# Na węźle worker-01:
sudo apt update
sudo apt install --only-upgrade containerd-stable
sudo systemctl restart containerd
sudo systemctl is-active containerd

# Z maszyny administracyjnej:
kubectl uncordon worker-01
kubectl get node worker-01 -o jsonpath='{.status.nodeInfo.containerRuntimeVersion}{"\n"}'

W środowisku z wysoką dostępnością warto wykonywać tę operację falami: najpierw jeden węzeł testowy, potem niewielka część puli, następnie reszta. Po aktualizacji sprawdź także zdarzenia poda i logi kubeleta, bo problemy z runtime potrafią objawić się jako ImagePullBackOff, błędy tworzenia sandboxa albo opóźnienia przy starcie kontenera.

Hardening po aktualizacji: co warto zmienić w politykach

Sama aktualizacja zamyka znane podatności, ale ten incydent jest dobrą okazją do przeglądu zasad uruchamiania obrazów. Administratorzy często traktują imagePullPolicy jako parametr wydajnościowy, tymczasem w kontekście zatrucia cache jest to również parametr bezpieczeństwa. Rekomendacje praktyczne:

  • Preferuj obrazy przypięte digestem, np. registry.example/app@sha256:..., zamiast samych tagów typu stable albo prod.
  • Ogranicz użycie imagePullPolicy: Never do kontrolowanych scenariuszy laboratoryjnych lub air-gapped, gdzie lokalny cache jest zarządzany centralnie.
  • W namespace’ach multi-tenant rozważ wymuszanie imagePullPolicy: Always albo dodatkowej walidacji digestów przez admission controller.
  • Nie dawaj szerokich uprawnień do tworzenia podów kontom CI, które nie muszą uruchamiać dowolnych obrazów i dowolnych konfiguracji.
  • Monitoruj różnice między obrazem zadeklarowanym w manifeście a obrazem faktycznie uruchomionym na węźle, szczególnie dla workloadów krytycznych.
  • Traktuj checkpoint/restore jako funkcję uprzywilejowaną, a nie neutralny mechanizm operacyjny dostępny dla każdego zespołu.

Jeżeli używasz Kyverno, OPA Gatekeeper albo natywnych mechanizmów admission, warto dopisać regułę odrzucającą pody z niejednoznacznymi tagami w krytycznych namespace’ach. Minimalny cel to zatrzymanie manifestów z :latest, bez digestu oraz z imagePullPolicy: Never poza wyjątkami. Nie trzeba robić rewolucji jednego dnia; wystarczy zacząć od trybu audytu i zebrać, które aplikacje wymagają poprawy pipeline’u.

Observability: czego szukać po stronie węzła

Po aktualizacji dobrze jest zweryfikować nie tylko numer wersji, ale też stan lokalnego magazynu obrazów. Na węźle można przejrzeć listę obrazów widzianą przez containerd oraz porównać ją z oczekiwanymi obrazami produkcyjnymi:

sudo ctr -n k8s.io images list
sudo crictl images
sudo journalctl -u containerd --since "2026-06-18" --no-pager | tail -n 200

W większych klastrach ręczne sprawdzanie nie wystarczy. Warto zebrać metrykę wersji runtime z każdego węzła do systemu inventory lub CMDB, a alert ustawić nie na sam numer CVE, lecz na warunek: „containerd w gałęzi 2.1 poniżej 2.1.9, 2.2 poniżej 2.2.5 albo 2.3 poniżej 2.3.2”. To zmniejsza szum i pozwala objąć także hosty niepodłączone do głównego klastra, np. maszyny CI uruchamiające lokalny containerd.

Podsumowanie praktyczne

Do 14 lipca 2026 r. rozsądne minimum dla zespołu Linux/Kubernetes jest jasne: zinwentaryzować wersje containerd, zaktualizować węzły do poprawek z 18 czerwca 2026 r. lub do wersji pakietów wskazanych przez dystrybucję, zrestartować runtime w kontrolowany sposób i przejrzeć polityki obrazów. CVE-2026-50195 pokazuje, że lokalny cache obrazów na węźle jest częścią modelu bezpieczeństwa klastra. Jeżeli w klastrze działa wielu użytkowników, wiele pipeline’ów CI albo workloady z różnych poziomów zaufania, aktualizacja containerd powinna mieć priorytet wyższy niż zwykły maintenance runtime.

Podsumowanie działań bezpieczeństwa po wydaniu poprawek containerd 2.3.2, 2.2.5 i 2.1.9
Najważniejsze działania: aktualizacja runtime, polityki obrazów i monitoring wersji na węzłach Linux.

Źródła i dalsza lektura

LinuxAdmin Online
Przegląd prywatności

Ta strona korzysta z ciasteczek, aby zapewnić Ci najlepszą możliwą obsługę. Informacje o ciasteczkach są przechowywane w przeglądarce i wykonują funkcje takie jak rozpoznawanie Cię po powrocie na naszą stronę internetową i pomaganie naszemu zespołowi w zrozumieniu, które sekcje witryny są dla Ciebie najbardziej interesujące i przydatne.