Naprawianie dziur w klastrze Kubernetes. Raport i transkrypcja z DevOpsConf

Pavel Selivanov, architekt rozwiązań Southbridge i nauczyciel Slurm, wygłosił prezentację na DevOpsConf 2019. Ta rozmowa jest częścią jednego z tematów szczegółowego kursu na temat Kubernetesa „Slurm Mega”.

Slurm Basic: wprowadzenie do Kubernetes odbędzie się w Moskwie w dniach 18-20 listopada.
Slurm Mega: spojrzenie pod maskę Kubernetesa — Moskwa, 22-24 listopada.
Slurm Online: oba kursy Kubernetes zawsze dostępne.

Poniżej fragmentu znajduje się transkrypcja raportu.

Dzień dobry Kolegom i tym, którzy im współczują. Dzisiaj będę mówić o bezpieczeństwie.

Widzę, że dzisiaj na sali jest wielu ochroniarzy. Z góry przepraszam, jeśli użyję terminów ze świata bezpieczeństwa nie do końca tak, jak jest to dla Ciebie zwyczajowe.

Tak się złożyło, że jakieś pół roku temu natknąłem się na jeden publiczny klaster Kubernetes. Publiczny oznacza, że ​​istnieje n-ta liczba przestrzeni nazw; w tych przestrzeniach nazw znajdują się użytkownicy izolowani w swojej przestrzeni nazw. Wszyscy ci użytkownicy należą do różnych firm. Otóż ​​założono, że ten klaster powinien służyć jako CDN. Oznacza to, że dają ci klaster, dają ci tam użytkownika, idziesz tam do swojej przestrzeni nazw i wdrażasz swoje fronty.

Moja poprzednia firma próbowała sprzedawać taką usługę. Poproszono mnie o szturchnięcie klastra i sprawdzenie, czy to rozwiązanie jest odpowiednie, czy nie.

Dotarłem do tego skupiska. Otrzymałem ograniczone prawa, ograniczoną przestrzeń nazw. Chłopaki tam zrozumieli, czym jest bezpieczeństwo. Przeczytali o kontroli dostępu opartej na rolach (RBAC) w Kubernetesie i przekręcili to tak, że nie mogłem uruchamiać podów oddzielnie od wdrożeń. Nie pamiętam problemu, który próbowałem rozwiązać, uruchamiając kapsułę bez wdrażania, ale naprawdę chciałem uruchomić tylko kapsułę. Na szczęście postanowiłem sprawdzić jakie mam prawa w klastrze, co mogę, czego nie mogę i co tam schrzanili. Jednocześnie powiem Ci, co nieprawidłowo skonfigurowali w RBAC.

Tak się złożyło, że w ciągu dwóch minut otrzymałem administratora do ich klastra, przejrzałem wszystkie sąsiednie przestrzenie nazw, zobaczyłem tam działające fronty produkcyjne firm, które już zakupiły usługę i wdrożyły. Ledwo mogłem się powstrzymać przed udaniem się na czyjś front i umieszczeniem przekleństw na stronie głównej.

Opowiem Ci na przykładach, jak to zrobiłem i jak się przed tym uchronić.

Ale najpierw pozwól, że się przedstawię. Nazywam się Paweł Seliwanow. Jestem architektem w Southbridge. Rozumiem Kubernetes, DevOps i różne fantazyjne rzeczy. Inżynierowie Southbridge i ja budujemy to wszystko i doradzam.

Oprócz naszej głównej działalności, niedawno uruchomiliśmy projekty o nazwie Slums. Staramy się przybliżyć masom naszą umiejętność pracy z Kubernetesem, aby nauczyć innych ludzi również pracy z K8.

O czym będę dzisiaj mówić? Temat raportu jest oczywisty – dotyczący bezpieczeństwa klastra Kubernetes. Ale chcę od razu powiedzieć, że ten temat jest bardzo duży - i dlatego chcę od razu wyjaśnić, o czym na pewno nie będę mówił. Nie będę już mówił o oklepanych terminach, które w Internecie zostały już użyte setki razy. Wszelkiego rodzaju RBAC i certyfikaty.

Opowiem o tym, co boli mnie i moich kolegów w kwestii bezpieczeństwa w klastrze Kubernetes. Problemy te widzimy zarówno wśród dostawców udostępniających klastry Kubernetes, jak i wśród klientów, którzy do nas przychodzą. A nawet od klientów, którzy przychodzą do nas z innych firm konsultingowo-administracyjnych. Oznacza to, że skala tragedii jest rzeczywiście bardzo duża.

Są dosłownie trzy punkty, o których dzisiaj porozmawiam:

  1. Prawa użytkownika a prawa poda. Prawa użytkownika i prawa poda to nie to samo.
  2. Zbieranie informacji o klastrze. Pokażę, że możesz zebrać wszystkie potrzebne informacje z klastra bez konieczności posiadania specjalnych uprawnień w tym klastrze.
  3. Atak DoS na klaster. Jeśli nie uda nam się zebrać informacji, i tak będziemy mogli założyć klaster. Opowiem o atakach DoS na elementy sterujące klastra.

Kolejna ogólna rzecz, o której wspomnę, to to, na czym to wszystko testowałem i na czym z całą pewnością mogę powiedzieć, że wszystko działa.

Za podstawę przyjmujemy instalację klastra Kubernetes przy użyciu Kubespray. Jeśli ktoś nie wie, to właściwie jest to zestaw ról dla Ansible. Wykorzystujemy go stale w naszej pracy. Dobrą rzeczą jest to, że można go zwinąć w dowolnym miejscu - można go zwinąć na kawałki żelaza lub gdzieś w chmurę. Jedna metoda instalacji działa w zasadzie na wszystko.

W tym klastrze będę mieć Kubernetes v1.14.5. Cały klaster Cube, który rozważymy, jest podzielony na przestrzenie nazw, każda przestrzeń nazw należy do osobnego zespołu, a członkowie tego zespołu mają dostęp do każdej przestrzeni nazw. Nie mogą przechodzić do różnych przestrzeni nazw, tylko do swojej własnej. Istnieje jednak pewne konto administratora, które ma uprawnienia do całego klastra.

Naprawianie dziur w klastrze Kubernetes. Raport i transkrypcja z DevOpsConf

Obiecałem, że pierwszą rzeczą, którą zrobimy, będzie uzyskanie praw administratora do klastra. Potrzebujemy specjalnie przygotowanego poda, który rozbije klaster Kubernetes. Wystarczy, że zastosujemy go do klastra Kubernetes.

kubectl apply -f pod.yaml

Ten kapsuła dotrze do jednego z masterów klastra Kubernetes. A po tym klaster z radością zwróci nam plik o nazwie admin.conf. W Cube plik ten przechowuje wszystkie certyfikaty administratora i jednocześnie konfiguruje API klastra. Tak łatwo jest uzyskać dostęp administracyjny do, jak sądzę, 98% klastrów Kubernetes.

Powtarzam, ten moduł został stworzony przez jednego programistę w twoim klastrze, który ma dostęp do wdrażania swoich propozycji w jednej małej przestrzeni nazw, a wszystko to jest blokowane przez RBAC. Nie miał żadnych praw. Mimo to certyfikat został zwrócony.

A teraz o specjalnie przygotowanym strąku. Działamy na dowolnym obrazie. Weźmy jako przykład debian:jessie.

Mamy taką rzecz:

tolerations:
-   effect: NoSchedule 
    operator: Exists 
nodeSelector: 
    node-role.kubernetes.io/master: "" 

Czym jest tolerancja? Wzorce w klastrze Kubernetes są zwykle oznaczone czymś, co nazywa się skażenie. Istotą tej „infekcji” jest to, że mówi, że podów nie można przypisać do węzłów głównych. Ale nikt nie zadaje sobie trudu, aby wskazać w jakimkolwiek kapsule, że jest tolerancyjny na „infekcję”. Sekcja Tolerancja mówi po prostu, że jeśli jakiś węzeł ma NoSchedule, to nasz węzeł jest odporny na taką infekcję - i nie ma żadnych problemów.

Co więcej, mówimy, że nasz under jest nie tylko tolerancyjny, ale także chce konkretnie celować w mistrza. Bo mistrzowie mają najsmaczniejszą rzecz, jakiej potrzebujemy – wszystkie certyfikaty. Dlatego mówimy nodeSelector - i mamy standardową etykietę na masterach, która pozwala wybrać ze wszystkich węzłów w klastrze dokładnie te węzły, które są masterami.

Z tymi dwoma sekcjami na pewno trafi do mistrza. I będzie mu wolno tam mieszkać.

Jednak samo przyjście do mistrza nam nie wystarczy. To nam nic nie da. Następnie mamy te dwie rzeczy:

hostNetwork: true 
hostPID: true 

Określamy, że nasz pod, który uruchamiamy, będzie żył w przestrzeni nazw jądra, sieciowej przestrzeni nazw i przestrzeni nazw PID. Gdy kapsuła zostanie uruchomiona na urządzeniu głównym, będzie mogła zobaczyć wszystkie rzeczywiste interfejsy tego węzła, nasłuchiwać całego ruchu i zobaczyć PID wszystkich procesów.

Wtedy to kwestia małych rzeczy. Weź etcd i czytaj, co chcesz.

Najciekawszą rzeczą jest ta funkcja Kubernetes, która jest tam domyślnie obecna.

volumeMounts:
- mountPath: /host 
  name: host 
volumes:
- hostPath: 
    path: / 
    type: Directory 
  name: host 

A jego istota polega na tym, że w podu, który uruchamiamy, nawet bez uprawnień do tego klastra, możemy powiedzieć, że chcemy utworzyć wolumin typu hostPath. Oznacza to pobranie ścieżki z hosta, na którym uruchomimy - i przyjęcie jej jako wolumenu. A potem nazywamy to nazwą: host. Montujemy całą tę ścieżkę hosta wewnątrz kapsuły. W tym przykładzie do katalogu /host.

Powtórzę to jeszcze raz. Powiedzieliśmy podowi, aby przyszedł do mastera, pobrał tam hostNetwork i hostPID - i zamontował cały katalog główny mastera w tym pod.

Rozumiesz, że w Debianie mamy uruchomiony bash, a ten bash działa pod rootem. Oznacza to, że właśnie otrzymaliśmy root na masterze, nie mając żadnych uprawnień w klastrze Kubernetes.

Następnie całe zadanie polega na przejściu do podkatalogu /host /etc/kubernetes/pki, jeśli się nie mylę, pobraniu tam wszystkich certyfikatów głównych klastra i odpowiednio zostaniu administratorem klastra.

Jeśli tak na to spojrzeć, to są to jedne z najniebezpieczniejszych praw w podach – niezależnie od tego, jakie uprawnienia posiada użytkownik:
Naprawianie dziur w klastrze Kubernetes. Raport i transkrypcja z DevOpsConf

Jeśli mam uprawnienia do uruchamiania poda w jakiejś przestrzeni nazw klastra, wówczas ten pod ma domyślnie te uprawnienia. Mogę uruchamiać uprzywilejowane pody i są to generalnie wszystkie uprawnienia, praktycznie root w węźle.

Moim ulubionym jest użytkownik root. Kubernetes ma tę opcję Uruchom jako użytkownik inny niż root. Jest to rodzaj ochrony przed hakerem. Czy wiesz, czym jest „wirus mołdawski”? Jeśli nagle zostaniesz hakerem i wejdziesz do mojego klastra Kubernetes, to my, biedni administratorzy, zapytamy: „Proszę wskazać w swoich podach, za pomocą których zhakujesz mój klaster, uruchom jako użytkownik inny niż root. W przeciwnym razie zdarzy się, że uruchomisz proces w swoim pod'u pod rootem i bardzo łatwo będzie ci mnie zhakować. Proszę, chroń się przed sobą.”

Wolumin ścieżki hosta jest moim zdaniem najszybszym sposobem na uzyskanie pożądanego rezultatu z klastra Kubernetes.

Ale co z tym wszystkim zrobić?

Myśl, która powinna przyjść do głowy każdemu normalnemu administratorowi, który spotyka się z Kubernetesem, brzmi: „Tak, mówiłem, Kubernetes nie działa. Są w nim dziury. A cały ten Cube to bzdury. W rzeczywistości istnieje coś takiego jak dokumentacja i jeśli tam spojrzysz, jest tam sekcja Polityka bezpieczeństwa kapsuł.

Jest to obiekt yaml - możemy go utworzyć w klastrze Kubernetes - który kontroluje aspekty bezpieczeństwa konkretnie w opisie podów. Oznacza to, że w rzeczywistości kontroluje prawa do korzystania z dowolnej sieci hostów, hostPID i niektórych typów woluminów znajdujących się w zasobnikach podczas uruchamiania. Wszystko to można opisać za pomocą Polityki Bezpieczeństwa Podów.

Najciekawszą rzeczą w Polityce bezpieczeństwa Podów jest to, że w klastrze Kubernetes wszystkie instalatory PSP nie tylko nie są w żaden sposób opisane, ale są po prostu domyślnie wyłączone. Polityka bezpieczeństwa kapsuły jest włączana za pomocą wtyczki wstępu.

OK, wdróżmy Politykę Bezpieczeństwa Podów w klastrze, powiedzmy, że mamy w przestrzeni nazw kilka podów usług, do których dostęp mają tylko administratorzy. Załóżmy, że we wszystkich innych przypadkach pody mają ograniczone prawa. Ponieważ najprawdopodobniej programiści nie muszą uruchamiać uprzywilejowanych podów w klastrze.

I wydaje się, że u nas wszystko jest w porządku. A naszego klastra Kubernetes nie da się zhakować w dwie minuty.

Tam jest problem. Najprawdopodobniej, jeśli masz klaster Kubernetes, w twoim klastrze jest zainstalowane monitorowanie. Posunę się nawet do przewidywania, że ​​jeśli twój klaster będzie monitorowany, będzie się nazywał Prometheus.

To, co ci powiem, będzie dotyczyć zarówno operatora Prometeusza, jak i Prometeusza w czystej postaci. Pytanie brzmi: jeśli nie uda mi się tak szybko wprowadzić administratora do klastra, oznacza to, że muszę szukać dalej. I mogę szukać za pomocą twojego monitoringu.

Prawdopodobnie wszyscy czytali te same artykuły na temat Habré, a monitorowanie znajduje się w przestrzeni nazw monitorowania. Wykres steru nazywa się mniej więcej tak samo dla wszystkich. Zgaduję, że jeśli zainstalujesz wersję stabilną/prometheus, otrzymasz mniej więcej te same nazwy. I najprawdopodobniej nie będę musiał nawet zgadywać nazwy DNS w twoim klastrze. Bo to standard.

Naprawianie dziur w klastrze Kubernetes. Raport i transkrypcja z DevOpsConf

Następnie mamy określony program deweloperski, w którym można uruchomić określony pod. Następnie z tego modułu bardzo łatwo jest zrobić coś takiego:

$ curl http://prometheus-kube-state-metrics.monitoring 

prometheus-kube-state-metrics to jeden z eksporterów Prometheusa, który zbiera metryki z samego interfejsu API Kubernetes. Jest tam mnóstwo danych, co działa w Twoim klastrze, co to jest, jakie masz z tym problemy.

Jako prosty przykład:

kube_pod_container_info{namespace=”kube-system”,pod=”kube-apiserver-k8s- 1″,container=”kube-apiserver”,image=

„gcr.io/google-containers/kube-apiserver:v1.14.5”

,image_id=»docker-pullable://gcr.io/google-containers/kube- apiserver@sha256:e29561119a52adad9edc72bfe0e7fcab308501313b09bf99df4a96 38ee634989″,container_id=»docker://7cbe7b1fea33f811fdd8f7e0e079191110268f2 853397d7daf08e72c22d3cf8b»} 1

Wykonując proste żądanie curl z nieuprzywilejowanego poda, możesz uzyskać następujące informacje. Jeśli nie wiesz, jakiej wersji Kubernetes używasz, z łatwością Ci to powie.

A najciekawsze jest to, że oprócz dostępu do metryk stanu kube, możesz równie łatwo uzyskać bezpośredni dostęp do samego Prometheusa. Można stamtąd zbierać dane. Na tej podstawie możesz nawet tworzyć wskaźniki. Nawet teoretycznie można zbudować takie zapytanie z klastra w Prometheusie, co po prostu je wyłączy. Twoje monitorowanie całkowicie przestanie działać z poziomu klastra.

I tu pojawia się pytanie, czy Twój monitoring monitoruje jakiś zewnętrzny monitoring. Właśnie dostałem możliwość działania w klastrze Kubernetes bez żadnych konsekwencji dla siebie. Nawet nie będziecie wiedzieć, że tam działam, bo nie ma już monitoringu.

Podobnie jak w przypadku PSP, wydaje się, że problem polega na tym, że wszystkie te fantazyjne technologie – Kubernetes, Prometheus – po prostu nie działają i są pełne dziur. Nie bardzo.

Jest coś takiego - Polityka sieci.

Jeśli jesteś zwykłym administratorem, to najprawdopodobniej wiesz o zasadach sieciowych, że jest to po prostu kolejny yaml, którego jest już sporo w klastrze. A niektóre zasady sieciowe zdecydowanie nie są potrzebne. I nawet jeśli przeczytałeś czym jest Network Policy, że jest to zapora yaml Kubernetesa, pozwala na ograniczenie praw dostępu pomiędzy przestrzeniami nazw, pomiędzy podami, to z pewnością zdecydowałeś, że zapora sieciowa w formacie yaml w Kubernetesie opiera się na kolejnych abstrakcjach ... Nie, nie. To zdecydowanie nie jest konieczne.

Nawet jeśli nie powiedziałeś swoim specjalistom ds. bezpieczeństwa, że ​​używając Kubernetesa, możesz zbudować bardzo łatwą i prostą zaporę ogniową, w dodatku bardzo szczegółową. Jeśli jeszcze tego nie wiedzą i nie przeszkadzają Ci: „No cóż, daj mi, daj mi…” W takim razie potrzebujesz Network Policy, aby zablokować dostęp do niektórych miejsc usług, które można wyciągnąć z Twojego klastra bez żadnego zezwolenia.

Podobnie jak w przykładzie, który podałem, możesz pobrać metryki stanu Kube z dowolnej przestrzeni nazw w klastrze Kubernetes bez żadnych uprawnień. Zasady sieciowe zamknęły dostęp ze wszystkich innych przestrzeni nazw do przestrzeni nazw monitorowania i to wszystko: brak dostępu, brak problemów. Na wszystkich istniejących wykresach, zarówno standardowym Prometeuszu, jak i Prometeuszu znajdującym się w operatorze, w wartościach steru znajduje się po prostu opcja umożliwiająca po prostu włączenie dla nich zasad sieciowych. Wystarczy to włączyć i będą działać.

Tak naprawdę jest tu jeden problem. Będąc zwykłym brodatym administratorem, najprawdopodobniej zdecydowałeś, że zasady sieciowe nie są potrzebne. Po przeczytaniu różnego rodzaju artykułów na temat zasobów takich jak Habr zdecydowałeś, że flanela, zwłaszcza w trybie bramy hosta, to najlepsza rzecz, jaką możesz wybrać.

Co robić?

Możesz spróbować ponownie wdrożyć rozwiązanie sieciowe, które masz w swoim klastrze Kubernetes, spróbować zastąpić je czymś bardziej funkcjonalnym. Na przykład dla tego samego Calico. Ale od razu chcę powiedzieć, że zadanie zmiany rozwiązania sieciowego w działającym klastrze Kubernetes jest dość nietrywialne. Rozwiązałem to dwukrotnie (za każdym razem jednak teoretycznie), ale nawet pokazaliśmy, jak to się robi w Slurms. Naszym studentom pokazaliśmy jak zmienić rozwiązanie sieciowe w klastrze Kubernetes. W zasadzie można spróbować zadbać o to, aby na klastrze produkcyjnym nie było przestojów. Ale prawdopodobnie ci się to nie uda.

A problem został rozwiązany w bardzo prosty sposób. W klastrze znajdują się certyfikaty i wiesz, że Twoje certyfikaty wygasną za rok. No i zwykle normalne rozwiązanie z certyfikatami w klastrze - po co się martwić, w pobliżu zbudujemy nowy klaster, pozwolimy staremu zgnić i ponownie wdrożymy wszystko. To prawda, że ​​​​kiedy się zepsuje, będziemy musieli usiąść przez jeden dzień, ale oto nowa gromada.

Kiedy podnosisz nowy klaster, włóż jednocześnie Calico zamiast flaneli.

Co zrobić, jeśli Twoje certyfikaty są wydawane na sto lat i nie zamierzasz ponownie wdrażać klastra? Istnieje coś takiego jak Kube-RBAC-Proxy. To bardzo fajne rozwiązanie, które pozwala na osadzenie się jako kontenera bocznego w dowolnym pod w klastrze Kubernetes. I faktycznie dodaje autoryzację do tego poda poprzez RBAC samego Kubernetes.

Jest jeden problem. Wcześniej to rozwiązanie Kube-RBAC-Proxy było wbudowane w Prometheusa operatora. Ale potem go nie było. Teraz nowoczesne wersje polegają na tym, że masz politykę sieciową i zamykasz ją za ich pomocą. Dlatego będziemy musieli trochę przepisać wykres. W rzeczywistości, jeśli pójdziesz do to repozytorium, istnieją przykłady wykorzystania tego jako przyczepek bocznych, a wykresy będą musiały zostać przepisane w minimalnym stopniu.

Jest jeszcze jeden mały problem. Prometheus nie jest jedyną firmą, która udostępnia swoje dane każdemu. Wszystkie nasze komponenty klastra Kubernetes mogą również zwracać własne metryki.

Ale jak już powiedziałem, jeśli nie możesz uzyskać dostępu do klastra i zebrać informacji, możesz przynajmniej wyrządzić jakąś krzywdę.

Dlatego szybko pokażę dwa sposoby zniszczenia klastra Kubernetes.

Będziesz się śmiać, kiedy ci to powiem, to są dwa przypadki z życia wzięte.

Metoda pierwsza. Wyczerpanie zasobów.

Uruchommy kolejny specjalny zasobnik. Będzie miał taki rozdział.

resources: 
    requests: 
        cpu: 4 
        memory: 4Gi 

Jak wiadomo, żądania to ilość procesora i pamięci zarezerwowana na hoście dla określonych podów z żądaniami. Jeśli w klastrze Kubernetes mamy czterordzeniowego hosta i przybędą tam cztery pody CPU z żądaniami, oznacza to, że więcej podów z żądaniami nie będzie mogło przyjść do tego hosta.

Jeśli uruchomię taki pod, uruchomię polecenie:

$ kubectl scale special-pod --replicas=...

Wtedy nikt inny nie będzie mógł wdrożyć rozwiązania w klastrze Kubernetes. Ponieważ wszystkim węzłom zabraknie żądań. I w ten sposób zatrzymam Twój klaster Kubernetes. Jeśli zrobię to wieczorem, mogę przerwać wdrażanie na dość długi czas.

Jeśli ponownie spojrzymy na dokumentację Kubernetesa, zobaczymy coś zwanego Limit Range. Ustawia zasoby dla obiektów klastra. Możesz napisać obiekt Limit Range w YAML, zastosować go do określonych przestrzeni nazw - i wtedy w tej przestrzeni nazw możesz powiedzieć, że masz domyślne, maksymalne i minimalne zasoby dla podów.

Za pomocą czegoś takiego możemy ograniczyć użytkownikom w określonych przestrzeniach nazw produktów zespołów możliwość wskazywania wszelkiego rodzaju nieprzyjemnych rzeczy na ich podach. Ale niestety, nawet jeśli powiesz użytkownikowi, że nie może uruchamiać podów z żądaniami dotyczącymi więcej niż jednego procesora, istnieje wspaniałe polecenie skalowania lub może skalować poprzez pulpit nawigacyjny.

I stąd właśnie wzięła się metoda numer dwa. Uruchamiamy 11 111 111 111 111 kapsuł. To jedenaście miliardów. Nie dlatego, że wymyśliłem taką liczbę, ale dlatego, że sam ją widziałem.

Prawdziwa historia. Późnym wieczorem miałem już wyjść z biura. Widzę grupę programistów siedzących w kącie i gorączkowo robiących coś ze swoimi laptopami. Podchodzę do chłopaków i pytam: „Co się z wami stało?”

Nieco wcześniej, około dziewiątej wieczorem, jeden z deweloperów przygotowywał się do powrotu do domu. I zdecydowałem: „Teraz zmniejszę moją aplikację do jednego”. Nacisnąłem jeden, ale Internet trochę zwolnił. Nacisnął ten ponownie, nacisnął ten i kliknął Enter. Szperałem we wszystkim, co mogłem. Potem ożył Internet – i wszystko zaczęło się skalować do tej liczby.

Co prawda ta historia nie miała miejsca na Kubernetesie, w tamtym czasie był to Nomad. Skończyło się na tym, że po godzinie naszych prób powstrzymania Nomada przed uporczywymi próbami skalowania, Nomad odpowiedział, że nie przestanie skalować i nie zrobi nic innego. „Jestem zmęczony, wychodzę”. I zwinął się.

Oczywiście próbowałem zrobić to samo na Kubernetesie. Kubernetes nie był zadowolony z jedenastu miliardów podów i powiedział: „Nie mogę. Przekracza wewnętrzne ochraniacze zębów. Ale 1 000 000 000 kapsułek mogłoby.

W odpowiedzi na miliard Sześcian nie zamknął się w sobie. Naprawdę zaczął skalować. Im dalej postępował proces, tym więcej czasu zajmowało mu tworzenie nowych strąków. Ale proces nadal trwał. Jedynym problemem jest to, że jeśli mogę uruchamiać pody bez ograniczeń w mojej przestrzeni nazw, to nawet bez żądań i ograniczeń mogę uruchamiać tak wiele podów z niektórymi zadaniami, że przy pomocy tych zadań węzły zaczną się sumować w pamięci, w procesorze. Kiedy uruchamiam tak wiele podów, informacje z nich powinny trafić do pamięci, to znaczy itp. A kiedy dociera tam zbyt dużo informacji, pamięć zaczyna wracać zbyt wolno – a Kubernetes zaczyna się nudzić.

I jeszcze jeden problem... Jak wiadomo elementy sterujące Kubernetesa to nie jedna centralna rzecz, ale kilka komponentów. W szczególności istnieje menedżer kontrolera, planista i tak dalej. Wszyscy ci goście zaczną jednocześnie wykonywać niepotrzebną, głupią pracę, która z czasem zacznie zajmować coraz więcej czasu. Menedżer kontrolera utworzy nowe pody. Harmonogram spróbuje znaleźć dla nich nowy węzeł. Najprawdopodobniej wkrótce zabraknie Ci nowych węzłów w klastrze. Klaster Kubernetes będzie zaczynał działać coraz wolniej.

Postanowiłem jednak pójść jeszcze dalej. Jak wiadomo w Kubernetesie istnieje coś takiego jak usługa. Cóż, domyślnie w Twoich klastrach najprawdopodobniej usługa działa przy użyciu tabel IP.

Jeśli na przykład uruchomisz miliard podów, a następnie użyjesz skryptu, aby zmusić Kubernetisa do utworzenia nowych usług:

for i in {1..1111111}; do
    kubectl expose deployment test --port 80  
        --overrides="{"apiVersion": "v1", 
           "metadata": {"name": "nginx$i"}}"; 
done 

Na wszystkich węzłach klastra coraz więcej nowych reguł iptables będzie generowanych mniej więcej jednocześnie. Co więcej, dla każdej usługi zostanie wygenerowany miliard reguł iptables.

Całość sprawdzałem na kilku tysiącach, do dziesięciu. I problem w tym, że już przy tym progu wykonanie ssh do węzła jest dość problematyczne. Ponieważ pakiety przechodzące przez tak wiele łańcuchów zaczynają sprawiać wrażenie niezbyt dobrego.

I to wszystko można rozwiązać za pomocą Kubernetesa. Istnieje taki obiekt przydziału zasobów. Ustawia liczbę dostępnych zasobów i obiektów dla przestrzeni nazw w klastrze. Możemy utworzyć obiekt yaml w każdej przestrzeni nazw klastra Kubernetes. Korzystając z tego obiektu, możemy powiedzieć, że mamy przydzieloną określoną liczbę żądań i limitów dla tej przestrzeni nazw, a następnie możemy powiedzieć, że w tej przestrzeni nazw możliwe jest utworzenie 10 usług i 10 podów. A pojedynczy deweloper może się chociaż wieczorami udusić. Kubernetes powie mu: „Nie możesz skalować swoich podów do tej ilości, ponieważ zasób przekracza limit”. To wszystko, problem rozwiązany. Dokumentacja tutaj.

W związku z tym pojawia się jeden problematyczny punkt. Czujesz, jak trudno jest stworzyć przestrzeń nazw w Kubernetesie. Aby go stworzyć, musimy wziąć pod uwagę wiele rzeczy.

Limit zasobów + zakres limitów + RBAC
• Utwórz przestrzeń nazw
• Utwórz wewnątrz zakres graniczny
• Utwórz wewnętrzny limit zasobów
• Utwórz konto serwisowe dla CI
• Utwórz powiązanie ról dla CI i użytkowników
• Opcjonalnie uruchom niezbędne moduły usług

Dlatego chciałbym skorzystać z okazji i podzielić się moimi osiągnięciami. Istnieje coś takiego jak operator SDK. Jest to sposób, w jaki klaster Kubernetes może pisać dla niego operatory. Możesz pisać instrukcje za pomocą Ansible.

Na początku był napisany w Ansible, a potem zobaczyłem, że istnieje operator SDK i przepisałem rolę Ansible na operatora. Ta instrukcja umożliwia utworzenie obiektu w klastrze Kubernetes zwanego poleceniem. Wewnątrz polecenia pozwala opisać środowisko dla tego polecenia w YAML. A w środowisku zespołowym pozwala nam to opisać, że alokujemy tak wiele zasobów.

Mały dzięki czemu cały ten złożony proces staje się łatwiejszy.

I na zakończenie. Co z tym wszystkim zrobić?
Pierwszy. Polityka bezpieczeństwa kapsuł jest dobra. I pomimo tego, że żaden z instalatorów Kubernetesa do dziś ich nie używa, to i tak trzeba je wykorzystać w swoich klastrach.

Zasady sieciowe to nie tylko kolejna niepotrzebna funkcja. Tego właśnie w klastrze naprawdę potrzeba.

LimitRange/ResourceQuota – czas to wykorzystać. Zaczęliśmy to stosować dawno temu i przez długi czas byłem pewien, że wszyscy z tego korzystają. Okazało się, że jest to rzadkie zjawisko.

Oprócz tego, o czym wspomniałem w raporcie, istnieją nieudokumentowane funkcje, które umożliwiają atak na klaster. Wydany niedawno obszerna analiza podatności Kubernetesa.

Niektóre rzeczy są bardzo smutne i bolesne. Na przykład, pod pewnymi warunkami, kostki w klastrze Kubernetes mogą udostępnić zawartość katalogu warlocks nieautoryzowanemu użytkownikowi.

Tutaj Istnieją instrukcje, jak odtworzyć wszystko, co ci powiedziałem. Istnieją pliki z przykładami produkcyjnymi tego, jak wygląda ResourceQuota i Polityka bezpieczeństwa podów. I tego wszystkiego możesz dotknąć.

Dziękuje za wszystko.

Źródło: www.habr.com

Dodaj komentarz