Blog

Błąd zaufania: Jak nasza wielodzierżawna konfiguracja Docker wyciekła dane (i jak to naprawiliśmy)

Dowiedz się, jak naiwna konfiguracja Docker jednego zespołu doprowadziła do wycieku danych między dzierżawcami i jak warstwowa strategia izolacji temu zapobiegła.

Podsumowanie

Kontenery Docker domyślnie nie są izolowane – współdzielą jądro hosta, a bez celowej konfiguracji dzierżawcy mogą zakłócać sobie nawzajem działanie. Ten artykuł opisuje rzeczywisty scenariusz, w którym dostawca hostingu wielodzierżawnego odkrył, że kontenery klientów mogły uzyskiwać dostęp do baz danych innych klientów z powodu wspólnej sieci i słabych domyślnych ustawień bezpieczeństwa. Pokazujemy krok po kroku zmiany, które naprawiły naruszenie: sieci zdefiniowane przez użytkownika dla każdego dzierżawcy, użytkownicy nie-root, usunięte zbędne uprawnienia, systemy plików tylko do odczytu i profile seccomp. Powszechne jest przekonanie, że kontenery zapewniają silną izolację; kwestionujemy to, wyjaśniając, dlaczego maszyny wirtualne wciąż oferują twardszą granicę i kiedy rozważyć podejście hybrydowe. Wnioski podkreślają, że izolacja to ćwiczenie warstwowe, a nie pojedyncze pole wyboru.

Incydent: Kiedy kontenery rozmawiają za dużo

Skonfigurowałeś Docker na jednym hoście, aby uruchomić wiele witryn klientów. Każdy klient ma własny kontener – schludne, izolowane środowisko, prawda? My też tak myśleliśmy. Aż do rutynowego audytu bezpieczeństwa, który ujawnił, że kontener Klienta A odczytywał gniazdo MySQL kontenera Klienta B na tym samym hoście. Dzielili domyślną sieć bridge. Co gorsza, kontenery działały jako root, więc atakujący, który przejął jeden, mógł manipulować gniazdem Docker hosta lub systemem plików innego kontenera. Naruszenie nie było wyrafinowanym exploitem – to była podstawowa błędna konfiguracja. Dane wyciekły. Zaufanie zniknęło.

Scenariusz awarii nie jest rzadki. Wiele zespołów zakłada, że przestrzenie nazw i cgroups Docker automatycznie odgradzają dzierżawców, ale nie doceniają, ile luk pozostaje otwartych domyślnie. Domyślne sieci bridge nie zapewniają izolacji sieciowej między kontenerami. Uruchamianie jako root daje kontenerowi więcej mocy niż potrzebuje. A bez jawnych limitów zasobów jeden hałaśliwy sąsiad może pozbawić inne CPU lub pamięci.

Krok 1: Zaprzestań współdzielenia pojedynczej sieci

Naszym pierwszym rozwiązaniem było przypisanie każdemu dzierżawcy własnej sieci Docker zdefiniowanej przez użytkownika. Zapobiega to komunikacji między kontenerami, chyba że jawnie je połączysz. Stworzyliśmy skrypt, który dla każdego dzierżawcy tworzy dedykowaną sieć i podłącza do niej ich kontener aplikacji. Kontener bazy danych znajduje się w tej samej sieci dzierżawcy, ale dodaliśmy również sieć wewnętrzną tylko do komunikacji wewnątrz dzierżawcy. Koniec z podglądaniem między dzierżawcami.

Odizolowaliśmy również bazy danych, uruchamiając je w osobnych kontenerach w tej samej sieci dzierżawcy, używając oddzielnych woluminów danych. Zapewniło to, że nawet jeśli atakujący włamie się do kontenera aplikacji, nie będzie mógł przechwycić ruchu bazy danych innego dzierżawcy.

Aby uzyskać bardziej szczegółowe informacje na temat strategii izolacji sieci, zobacz Praktyczna lista kontrolna izolacji Docker dla hostingu wielodzierżawnego.

Krok 2: Usuń zbędne uprawnienia

Domyślnie kontenery Docker działają z ograniczonym zestawem możliwości Linuksa, ale wciąż mają ich więcej niż większość aplikacji potrzebuje. Nasze kontenery działały jako root, co pozwalało procesom wewnątrz na wykonywanie działań takich jak montowanie systemów plików czy zmiana parametrów jądra. Przeszliśmy na uruchamianie aplikacji jako użytkownik nie-root wewnątrz kontenera (używając dyrektywy USER w pliku Dockerfile) i usunęliśmy wszystkie możliwości poza absolutnie niezbędnymi. Dla typowej aplikacji internetowej mogą to być tylko NET_BIND_SERVICE (do wiązania portów poniżej 1024) i CHOWN (do zapisu do katalogów). Dodaliśmy również --security-opt no-new-privileges, aby zapobiec eskalacji uprawnień.

Ten krok sam w sobie wyeliminował wiele typowych wektorów ucieczki z kontenera. Atakujący, który przejmie serwer WWW, nie może instalować pakietów, modyfikować binariów systemowych ani uzyskiwać dostępu do gniazda Docker hosta, ponieważ procesowi brakuje możliwości CAP_SYS_ADMIN lub CAP_DAC_OVERRIDE.

Krok 3: Zablokuj system plików

Zapisywalne systemy plików są częstą powierzchnią ataku. Ustawiliśmy główny system plików jako tylko do odczytu (--read-only) dla wszystkich kontenerów, a następnie zamontowaliśmy tymczasowe systemy plików (tmpfs) dla katalogów wymagających zapisu, takich jak /tmp i katalog pamięci podręcznej aplikacji. Zapobiega to modyfikowaniu kodu aplikacji lub utrwalaniu złośliwych plików binarnych przez atakującego.

Dodatkowo użyliśmy opcji --mount Dockera do podmontowania wrażliwych katalogów, takich jak gniazdo Docker, tylko wtedy, gdy jest to absolutnie konieczne – i nigdy w kontenerach produkcyjnych. Zasada: jeśli kontener nie musi zapisywać do ścieżki, ustaw ją jako tylko do odczytu.

Krok 4: Zastosuj profile Seccomp i AppArmor

Domyślne profile seccomp już blokują wiele niebezpiecznych wywołań systemowych, ale dostosowaliśmy je dalej, aby dopuścić tylko te wywołania, których nasza aplikacja rzeczywiście potrzebuje. Jest to kompromis, ponieważ wymaga profilowania aplikacji. Prostszym podejściem jest użycie domyślnego profilu seccomp Dockera, a następnie dodanie --security-opt seccomp=ścieżka/do/profil.json, jeśli potrzebujesz bardziej rygorystycznych zasad. Podobnie profile AppArmor mogą ograniczać procesy kontenera do konkretnych ścieżek plików i możliwości. Włączyliśmy AppArmor i użyliśmy niestandardowego profilu, który ograniczał dostęp tylko do katalogów danych aplikacji.

Aby uzyskać kompleksowy przewodnik po tych krokach hartowania, zapoznaj się z Hartowanie kontenerów Docker dla hostingu wielodzierżawnego: przewodnik izolacji krok po kroku.

Kontrarian: Czasami potrzebujesz maszyn wirtualnych

Niezależnie od stopnia hartowania, kontenery współdzielą jądro hosta. Luka w jądrze może złamać całą izolację naraz. Dlatego wiele platform dbających o bezpieczeństwo uruchamia kontenery wewnątrz lekkich maszyn wirtualnych – każdy dzierżawca ma własne jądro. To zwiększa narzut, ale zapewnia granicę na poziomie sprzętu, której kontenery same nie mogą zapewnić. Jeśli Twoi dzierżawcy przetwarzają dane kart kredytowych lub dokumentację medyczną, podejście hybrydowe (kontenery w maszynach wirtualnych) może być właściwym wyborem. Nie zakładaj, że izolacja kontenerów jest wystarczająca dla Twojego modelu zagrożeń; oceń wrażliwość danych i wymagania regulacyjne.

Aby uzyskać głębsze porównanie poziomów izolacji, przeczytaj Projektowanie wielodzierżawnej architektury Docker: wybór odpowiedniego poziomu izolacji.

Podsumowanie: Izolacja to stos, nie przełącznik

Naprawa nie była pojedynczą zmianą – to nakładanie warstw: izolacja sieci, ograniczone uprawnienia, systemy plików tylko do odczytu i filtrowanie wywołań systemowych. Nawet wtedy przyjęliśmy, że idealna izolacja jest niemożliwa w przypadku kontenerów współdzielących jądro. Dla naszych dzierżawców o najwyższym poziomie bezpieczeństwa przenieśliśmy ich na dedykowane hosty. Lekcja: nie ufaj domyślnym ustawieniom. Audytuj swoją konfigurację Docker tak, jakby naruszenie już nastąpiło. Czas na zaostrzenie zabezpieczeń jest przed wyciekiem, nie po.

Sources (5)