Docker Security #1

Maciej Lelusz
21. czerwca 2018
Reading time: 8 min
Docker Security #1

Każde nowe rozwiązanie, które choćby wykazuje delikatną chęć zagrzania miejsca w naszej produkcyjnej infrastrukturze – czy to w Datacenter, czy też w Chmurze, wywołuje w nas obronne uczucie niechęci i pogardy. To zachowawcze i zdrowe. Niektóre rozwiązania pozostają w tej strefie i jakoś nasza infrastruktura je odrzuca niczym nieudany przeszczep. Niektóre jednak, z różnych powodów zdają się naturalnie pasować. Przechodzą próbę stabilności, użytkowości i na końcu często na bardzo długo budzą wątpliwości bezpieczeństwa i poddawane są próbom na wielu płaszczyznach. Zdecydowana mniejszość przechodzi ten test i wstępują na panteon „naszych”, tych sprawdzonych, prawilnych rozwiązań. Najważniejsze jednak by dać im szanse i poznać mocne i słabe strony, a nie tylko negować i powtarzać głupoty zasłyszane z echa meandrów internetu.

Wszyscy słyszeliśmy, że Docker to zło i jest niedobry, niebezpieczny, niestabilny i generalnie jak by się tak głębiej nad tym zastanowić, to jest on puszką Pandory i przypisane są mu wszelkie negatywne epitety… ale czy nie tak samo było z Wirtualizacją, czy też sieciami Overlay? Warto spojrzeć na aktualną sytuację z Dockerem przez choćby te właśnie przykłady z kart historii. Zatem otwórzmy trochę głowy i pozwólmy sobie na małą przygodę z Dockerem, a potem go krytycznie oceniajmy. Nie rozwodząc się nad filozofią czym jest bezpieczeństwo, bo to jak badanie zawartości cukru w cukrze. Przyjmijmy, że dotykamy czubka góry lodowej w kontekście bezpieczeństwa kontenerów na warstwie konfiguracyjno-operacyjnej.

Zacznijmy jednak od tego, aby poznać diabła – przypomnijmy sobie, jak skonstruowany i gdzie w stosie programowym naszej infrastruktury mieszka Docker.

Wprowadźmy od razu nazewnictwo, Host to dla nas serwer fizyczny lub maszyna wirtualna, generalnie niema to znaczenia liczy się system operacyjny w kontekście konteneryzacji i nazywany jest on „Hardware Land”, w nim uruchamiany jest program nazywany „Docker Engine” lub „Docker Daemon” silnik ten umożliwia uruchamianie „Docker Image” – obrazów zawierających aplikacje, które, w momencie gdy, się już uruchomią nazywane są „Kontenerami”. Składają się one z warstw, dla uproszczenia przyjmijmy, że są dwie – binaria i bilioteki niezbędne do uruchomienia Aplikacji, oraz ona sama, która też jest swoistą warstwą. Obracając się w analogii do wirtualizacji, Docker Engine jest swego rodzaju serwisem uruchamiającym na Hardware Land będącym hypervisorem Docker Image, czyli pliki z obrazami maszyn wirtualnych, które w świcie Dockera są kontenerami. Sugestywna, lecz w szczegółach nieco myląca analogia… ale o tym zaraz, tymczasem postawmy sobie małe środowisko konteneryzacyjne. Proponuje maszynę wirtualną, poniższe komendy pasują do Ubuntu, ale można je łatwo zmapować, do któregokolwiek systemu z rodziny Linux:

[gdlr_core_code style=”light” ]

apt-get remove docker docker-engine docker.io

apt-get install apt-transport-https ca-certificates curl software-properties-common

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add –

apt-key fingerprint 0EBFCD88

add-apt-repository “deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable”

sudo apt-get install docker-ce

[/gdlr_core_code]

Dzięki powyższym komendom usuniemy ewentualne zainstalowane już wersje Dockera, następnie dodamy brakujące pakiety, by w kolejnym kroku dodać niezbędny klucz do bazy repozytoriów Ubuntu. Następnie repozytorium i już instalujemy Docker Engine, a teraz czas na pierwszy kontener:

[gdlr_core_code style=”light” ]

docker pull nginx

docker run -d -P -p 8080:80 nginx

docker container list

curl http://localhost:8080

docker exec -it [id] bash

touch /home/aaa

[/gdlr_core_code]

Dzięki temu zabiegowi ściągnęliśmy z publicznego Docker Hub, czyli ogólnodostępnej biblioteki obrazów Dockera, kontener zawierający w sobie Nginx’a. Następnie uruchomiliśmy go w trybie demona (-d), czyli w tle co pozwoliło nam pozostać w konsoli systemu operacyjnego Hardware Land, oraz zmapowaliśmy port 80 kontenera na port 8080. W kolejnym korku wyświetliliśmy listę uruchomionych kontenerów gdzie zobaczyliśmy ID naszego Nginxa, sprawdziliśmy, że działa pakując się na localhost i port 8080. Następnie weszliśmy do konsoli, podając ID kontenera i stworzyliśmy w jego środku plik.

Skoro już przypomnieliśmy sobie jak to generalnie wygląda i gdzie się co znajduje to kilka słów o tym, jakie są niektóre z możliwych wektorów ataku, czyli jak naszą infrastrukturę opartą o Dockera można zepsuć, zbrukać, zgnieść i wypluć.

Włamanie do kontenera

Zacznijmy od warstwy górnej, czyli kontenera. Można się do niego po prostu włamać, spowodowane jest to dokładnie tymi samymi czynnikami jak w przypadku aplikacji znajdującej się w maszynie wirtualnej, lub zainstalowanej na hoście fizycznym. Prosta sprawa – dziura w bibliotece, aplikacji spowodowane starym softem, lub jego nie do końca znanym pochodzeniem. Można również, tak jak z tradycyjnych środowisk usunąć po sobie logi, aby nie został żaden ślad. Na razie jest znajomo i komfortowo, znamy to z życia codziennego i w sumie chyba na nikim nie robi to zasadniczo wielkiego wrażenia.

Kernel exploits

Tutaj pragnę wrócić do poprzedniego akapitu i porównania do wirtualizacji. Konteneryzacja logicznie zachowuje się podobnie do wirtualizacji, ale na tym koniec – tu nie ma fizycznej separacji kernela, tak jak to ma się w starych dobrych hyperviorach. Jeden kontener to proces w systemie, a nie oddzielny korzystający z dedykowanych sprzętowych funkcji procesora byt tylko wciąż proces współdzielący jądro z wszystkimi innymi kontenerami w obrębie hosta. Czyli np. kontener, który będzie w stanie dokonać kernel panic pociągnie za sobą w nicość nie tylko siebie, ale całego hosta i wszystkie kontenerki na nim działające. To samo się tyczy dziury w kernelu – jeden kontener będzie mógł przez tę dziurę uzyskać dostęp do Hardware Land jak również i do innych kontenerów w obrębie trefnego Hosta.

Ucieczka z kontenera

Zabawa zaczyna się, gdy okazuje się, że możemy z wnętrza kontenera uzyskać dostęp do Hardware Land lub innego kontenera. Dzieje się tak, ponieważ domyślnie użytkownicy nie są zamknięci w namespace, dlatego jeżeli jakoś proces będzie w stanie uciec z kontenera, to będzie on posiadał takie same uprawniania jak w kontenerze… Werble… Światła… Kontener uruchomiony na użytkowniku root, ma uprawniania roota również w systemie operacyjnym Hardware Land. Dalej, z lekkim uśmieszkiem, możemy sobie wyobrazić jak wiele zła można wyrządzić – poczynając od małej ekskursji do innych kontenerów, sieci, hostów po zaburzenie działania serwera i jego agonalną śmierć wraz z całym majdanem konteneryzacyjnym na nim działającym… Drogi czytelniku, powstrzymaj się jeszcze przed oceną, bo szczerze mówiąc nic w tym niesamowitego. Aplikacja w kontenerze działająca na uprawnieniach root? To jest prawdziwy problem, coś jak dawanie uprawnień „Administratora komputera” dla stażysty, tylko że na sterydach, amfetaminie i kosie. Tak w ogólnie nie powinno się wydarzyć – to ma szanse zepsuć w zasadzie każdą infrastrukturę. Jednak pamiętaj o tym i podczas projektowania infrastruktury planuj ją wokół założenia, że ucieczka z kontenera jest przypadłością rzadką, ale możliwą.

Atak Denial-of-service

Kontenery dzielą zasoby hosta, na którym są uruchomione. Jak już sobie wyjaśniliśmy sytuację, z kernel panic, tak tutaj sprawa jest nieco subtelniejsza. Zasoby można konsumować różnie – sam kontener może podczas zwykłego działania zjeść wszystko, co dostanie i zadusić hosta. Tę samą sytuację może wykorzystać nasz chuligan, barbarzyńca, gorszyciel, pożerając swoimi niecnymi czynami cały dostępny RAM czy też moc CPU.

Zabezpieczenie poświadczeń

Wyobraź sobie sytuację, że Twój pierwszy kontener, który już zainstalowałeś, będzie posiadał na pokładzie aplikację, która będzie wymagać połączenia z bazą danych, czy jakimś innym zewnętrznym zasobem. Kontener będzie musiał posiadać w swojej konfiguracji hasło. Po pierwsze trzeba je tam umieścić i to najlepiej tak, aby nikt go nie znał poza właścicielem zasobu, a co więcej dobrze by było, aby hasło to było tymczasowe, powoływane na zazwyczaj krótki okres życia kontenera.

Artykuł został opublikowany na łamach IT Professional.

Poniżej znajdą Państwo linki do wcześniejszych artykułów: