Запълваме дупки в клъстера Kubernetes. Доклад и транскрипция от DevOpsConf

Павел Селиванов, архитект на решения за Southbridge и преподавател по Слёрм, направи презентация на DevOpsConf 2019. Тази презентация е част от темите на углубения курс по Kubernetes „Слёрм Мега“.

Слёрм Базов: Въведение в Kubernetes се провежда в Москва от 18 до 20 ноември.
Слёрм Мега: Заглеждаме под капака на Kubernetes — Москва, от 22 до 24 ноември.
Слёрм Онлайн: И двата курса по Kubernetes са винаги достъпни.

Пуснете видеото

Под катаклизма — транскрипция на презентацията.

Добър ден, колеги и симпатизанти. Днес ще говоря за сигурността.

Виждам, че в залата днес има много специалисти по сигурността. Извинявам се предварително, ако термините от света на сигурността не ги използвам точно както е прието при вас.

Случи се така, че преди около шест месеца попадам на един публичен кластер Kubernetes. Публичен означава, че там има n-брой namespaces, в тези namespaces са потребителите, изолирани в техния namespace. Всички тези потребители принадлежат на различни компании. Предполагаше се, че този кластер трябва да бъде използван като CDN. Тоест, дават ти кластер, дават ти потребител, идваш в своя namespace, деплоиш своите фронтове.

На предишната си компания се опитаха да продадат такава услуга. И мен ми помолиха да тествам кластер на принципа — подхожда ли или не подхожда такова решение.

Влязох в този кластер. Получих ограничени права, ограничен namespace. Там момчетата разбират какво е сигурност. Чели са какво е Role-based access control (RBAC) в Kubernetes и са го настроили така, че не можех да стартирам подове отделно от деплойментите. Не помня задачата, която се опитвах да реша, стартирайки под без деплоймент, но много исках просто да стартирам под. Реших на късмет да погледна какви права имам в кластера, какво мога, какво не мога, какво са настроили. Заедно с това ще разкажа какво имат неправилно настроено в RBAC.

Случи се така, че след две минути получих администраторски достъп до техния кластер, разгледах всичките съседни namespaces, видях там стартирани продакшън фронтове на компании, които вече са купили услугата и са деплоирани. Едва се сдържах да не отида при някого на фронта и на главната страница да не сложа някое нецензурно слово.

Ще разкажа с примери как го направих и как от това трябва да се защитаваме.

Но за начало, искам да се представя. Името ми е Павел Селиванов. Аз съм архитект в компанията Southbridge. Разбирам от Kubernetes, DevOps и всякакви модерни технологии. Заедно с инженерите на Southbridge работим върху тези технологии, а аз давам консултации.

Освен основната ни дейност, наскоро стартирахме проектите, наречени Слёрми. Опитваме се да внесем нашите умения за работа с Kubernetes в масите, да обучим и други хора да работят с K8s.

Днес ще говоря за тема, която е очевидна — за безопасността на Kubernetes кластера. Но веднага искам да заявя, че темата е много обширна и затова ще уточня за какво няма да говоря. Няма да обсъждам известни термини, които в интернет вече са били многократно разисквани. Различни RBAC и сертификати.

Ще говоря за нещата, които мен и моите колеги притесняват във връзка с безопасността на Kubernetes кластера. Тези проблеми виждаме както при доставчиците на Kubernetes кластери, така и при клиентите, които идват при нас. И дори при клиентите, които идват от други консултантски компании. Тоест, мащабът на трагедията всъщност е голям.

Буквално три точки, за които ще говоря днес:

  1. Права на потребителите vs права на pod-ове. Правата на потребителите и правата на pod-овете не са едно и също.
  2. Събиране на информация за кластера. Ще покажа как може да се събере всяка информация от кластера, която е необходима, без да имате специални права в него.
  3. DoS атака на кластера. Ако не можем да събираме информация, все пак можем да поставим кластера в неработоспособно състояние. Ще говоря за DoS атаки на управляващите елементи на кластера.

Още нещо общо, за което ще спомена — на какво съм тествал всичко това и на какво можем да кажем, че работи.

За основа вземаме инсталацията на Kubernetes кластер чрез Kubespray. Ако някой не знае, това всъщност е набор от роли за Ansible. Ние го използваме постоянно в работата си. Добро е, защото може да се инсталира където и да е — както на физически устройства, така и в облака. Един начин на инсталация е подходящ в принцип за всичко.

В този клъстер ще имам Kubernetes v1.14.5. Целият клъстер Куба, който ще разгледаме, е разделен на неймспейсове, всеки неймспейс принадлежи на отделен екип, а всеки екип има достъп до своя неймспейс. Не могат да влизат в различни неймспейсове, само в своя. Но има един администраторски акаунт, който има права за целия клъстер.

Запълваме дупки в клъстера Kubernetes. Доклад и транскрипция от DevOpsConf

Обещах, че първото нещо, което ще направим, е да получим администраторски права на клъстера. Нуждаем се от специално подготвен pod, който ще компрометира клъстера Kubernetes. Всичко, което трябва да направим, е да го приложим в клъстера Кубернетес.

kubectl apply -f pod.yaml

Този pod ще пристигне на един от майсторите на клъстера Kubernetes. И след това клъстерът радостно ще ни върне файл, наречен admin.conf. В Куба в този файл се съхраняват всички администраторски сертификати, а също така е настроен API на клъстера. Така просто можем да получим администраторски достъп, мисля, че за 98% от клъстерите Kubernetes.

Още веднъж, този pod е направен от един разработчик във вашия клъстер, който има достъп да насочва своите предложения в един малък неймспейс, който е напълно ограничен от RBAC. Нямаше никакви права. Но въпреки това, сертификатът се върна.

А сега за специално подготвения pod. Стартираме на всякакъв образ. Например, можем да вземем debian:jessie.

Имаме нещо такова:

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

Какво е toleration? Майсторите в клъстера Kubernetes обикновено са маркирани с нещо, наречено taint («замърсяване» на английски). Същността на това «замърсяване» е, че не може да се насочват pods на майсторските ноди. Но никой не пречи в който и да е pod да посочи, че е толерантен към «замърсяването». Секцията Toleration точно показва, че ако на някоя нода е зададено NoSchedule, то нашият pod е толерантен към такова замърсяване – и няма проблеми.

След това казваме, че нашият pod не е просто толерантен, но и иска специално да попада на майстора. Защото на майсторите се намират най-вкусните неща, от които имаме нужда – всички сертификати. Следователно казваме nodeSelector – и имаме стандартен етикет на майсторите, който позволява да изберем от всички ноди в клъстера именно тези, които са майстори.

С такива две секции pod със сигурност ще пристигне на майстора. И ще му бъде разрешено да живее там.

Но просто да пристигнем на майстора не е достатъчно. Това няма да ни даде нищо. Затова следват две неща:

hostNetwork: true 
hostPID: true 

Указваме, че нашият под, който стартираме, ще живее в пространството на името на ядрото, в пространството на името на мрежата и в пространството на името на PID. След като подът стартира на мастера, той ще може да вижда всички действителни, живи интерфейси на този възел, да прослушва целия трафик и да вижда PID на всички процеси.

Следващата стъпка е лесна. Вземате etcd и четете каквото искате.

Най-интересното е тази възможност на Kubernetes, която по подразбиране е налична там.

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

Същността е, че можем в пода, който стартираме, дори без права в този кластер, да кажем, че искаме да създадем volume от тип hostPath. Тоест, да вземем пътя от хоста, на който ще стартираме — и да го използваме като volume. И след това го наричаме name: host. Целият този hostPath го монтираме вътре в пода. В този пример в директория /host.

Още веднъж повтарям. Казахме на пода да пристигне на мастера, да получи hostNetwork и hostPID — и целият root на мастера да се монтира вътре в този под.

Разбирате, че в Debian имаме стартиран bash, и този bash работи под root. Тоест, току-що получихме root на мастера, без да притежаваме каквито и да било права в кластера Kubernetes.

Следващата задача е да влезете в пода в директория /host /etc/kubernetes/pki, ако не се лъжа, и да вземете всички сертификати на кластерния мастер и, съответно, да станете администратор на кластера.

Ако се замислите, това са едни от най-опасните права в подовете — независимо какви права има потребителят:
Запълваме дупки в клъстера Kubernetes. Доклад и транскрипция от DevOpsConf

Ако имам права да стартирам под в някакво пространство на имена на кластера, тези права по подразбиране съществуват за този под. Мога да стартирам привилегировани подове, а това означава всичките права, практически root на възела.

Моето любимо — Root user. А Kubernetes има опция Run As Non-Root. Това е вид защита срещу хакери. Знаете ли какво е "молдавски вирус"? Ако сте хакер и влезете в моя кластер Kubernetes, ние, бедните администратори, молим: "Моля, посочете в вашите подове, с които ще атакувате моя кластер, run as non-root. Иначе ще стане така, че ще стартирате процес в своя под под root и ще ми е много лесно да бъдете хакнат. Защитете се, моля, сами."

Host path volume — според мен, най-бързият начин да получите желан резултат от Kubernetes кластер.

Но какво да правим с всичко това?

Мисли, които трябва да идват на ум на всеки нормален администратор, който се сблъсква с Кубернетес: «Аха, казах ли не, че Кубернетес не работи. В него има проблеми. И всичко е безсмислено». Всъщност, съществува нещо като документация, а ако погледнете там, ще намерите раздел Политика за сигурност на подовете.

Това е yaml-обект — можем да го създадем в кластера Кубернетес — който контролира аспектите на сигурността именно в описанието на подовете. Тоест, всъщност контролира правата за използване на различни hostNetwork, hostPID, определен тип volume, които имат подовете при стартиране. С помощта на Политиката за сигурност на подовете всичко това може да бъде описано.

Най-интересното за Политиката за сигурност на подовете е, че в кластерите Кубернетес всички инстанции на PSP не само не са описани, те просто са изключени по подразбиране. Политиката за сигурност на подовете се активира чрез admission plugin.

Добре, ще инсталираме Политика за сигурност на подовете в кластера, да речем, че имаме служебни подове в неймспейса, до който имат достъп само администраторите. Да кажем, че в останалите подове правата са ограничени. Защото вероятно разработчиците не трябва да стартират привилегировани подове в вашия кластер.

И изглежда, че всичко е наред. И нашият Кубернетес кластер не може да бъде хакнат за две минути.

Има проблем. Вероятно, ако имате кластер Кубернетес, в него е инсталирано мониторинг. Дори мога да предскажа, че ако в кластера ви има мониторинг, той се нарича Prometheus.

Това, което ще разкажа сега, ще важи и за оператора Prometheus, и за Prometheus, инсталиран в чист вид. Въпросът е, че ако не мога толкова бързо да получа администратор в кластера, това означава, че трябва да търся повече. А мога да търся с помощта на вашия мониторинг.

Вероятно всички са чели едни и същи статии в Хабра, и мониторингът се намира в неймспейса monitoring. Helm chart-ът на всички се нарича донякъде по един и същ начин. Предполагам, че ако направите helm install stable/prometheus, ще получите приблизително едни и същи имена. И дори по-вероятно е, че DNS-имената в кластера ви няма да е необходимо да отгатвам. Защото е стандартно.

Запълваме дупки в клъстера Kubernetes. Доклад и транскрипция от DevOpsConf

След това имаме някакъв dev ns, в който можем да стартираме под. И след това от този под е много лесно да направим следното:

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

prometheus-kube-state-metrics — това е един от експортерите на Prometheus, който събира метрики от API на самия Kubernetes. Там има много данни за това, какво е разположено в клъстера ви, какво представлява то и какви проблеми имате с него.

Като прост пример:

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:e29561119a52adad9edc72bfe0e7fcab308501313b09bf99df4a9638ee634989″,container_id=»docker://7cbe7b1fea33f811fdd8f7e0e079191110268f2853397d7daf08e72c22d3cf8b»} 1

Извършвайки прост HTTP заявка с curl от непривилегирован под, можете да получите такава информация. Ако не знаете в коя версия на Kubernetes работите, той лесно ще ви каже.

А най-интересното е, че освен че се обръщате към kube-state-metrics, можете с еднакъв успех да се обърнете и директно към Prometheus. Можете да съберете метрики от него. Дори можете да извлечете метрики от него. Теоретично е възможно да изградите такава заявка от клъстера към Prometheus, която просто да го изключи. И вашият мониторинг напълно ще спре да работи от клъстера.

И тук вече възниква въпросът, следи ли някакво външно наблюдение вашия мониторинг. Току-що получих възможността да действам в клъстера на Kubernetes без никакви последствия за себе си. Вие дори няма да разберете, че действам там, тъй като мониторингът вече не съществува.

Точно както с PSP, възниква усещането, че проблемът е в това, че всички тези модни технологии — Kubernetes, Prometheus — просто не работят и са пълни с дупки. Всъщност обаче не е така.

Има нещо подобно — Network Policy.

Ако сте нормален администратор, вероятно знаете, че Network Policy е поредният yaml, който в клъстера вече е достатъчно много. И определено не са нужни много Network Policies. А дори и да сте прочели какво е Network Policy, че е yaml-брандмауер на Kubernetes, който позволява да ограничавате правата на достъп между пространства, между подове, определено вече сте решили, че брандмауер от типа yaml в Kubernetes е просто още един абстракция... Не, не. Определено не е нужно.

Дори и ако на вашите специалисти по сигурността не им е казано, че с вашия Кубернетес можете много лесно и просто да изградите защитна стена, и то с много гранулирана структура. Ако те все още не знаят за това и не ви досаждат: „Ами дайте, дайте…“ То в какъвто и случай, мрежовата политика ви е необходима, за да ограничите достъпа до някои служебни места, до които може да се получи достъп от вашия клъстер без никаква авторизация.

Както в примера, който споменах, можете да извлечете kube state metrics от всяко неймспейс в клъстера Кубернетес, без да имате права за това. Мрежовите политики затвориха достъпа от всички останали неймспейсове до неймспейса за мониторинг и по същество всичко: няма достъп, няма проблеми. Във всички чартове, които са налични, както в стандартния прометеус, така и в този, който е в оператора, просто в values на хелма има опция да активирате мрежови политики за тях. Трябва просто да ги включите и те ще работят.

Наистина има един проблем. Будейки нормален бородат администратор, вие вероятно сте решили, че мрежовите политики не са нужни. И прочитайки всякакви статии на ресурси като Хабр, вие сте решили, че flannel, особено с режим host-gateway, е най-доброто, което можете да изберете.

Какво да правим?

Можете да опитате да преразположите мрежовото решение, което имате в клъстера Кубернетес, и да го замените с нещо по-функционално. Например с Calico. Но веднага бих искал да кажа, че задачата да смените мрежовото решение в работещ клъстер на Кубернетес е доста нетривиална. Аз я решавах два пъти (и двата пъти, обаче, теоретично), но дори и на Слёрмах показвахме как да го направите. На нашите обучаеми показвахме как да сменят мрежовото решение в клъстера Кубернетес. В принципе можете да опитате да направите така, че в продукционния клъстер да няма прекъсвания. Но вероятно няма да успеете.

И проблемът всъщност се решава много просто. В клъстера има сертификати и вие знаете, че сертификатите ви ще изтекат след година. Обикновено нормалното решение с сертификатите в клъстера е: защо да се притесняваме, ще вдигнем нов клъстер наблизо, в стария да изтече и всичко да се преразположи. Истина е, че когато истекът, ден ще стои всичко изоставено, но ето ви новия клъстер.

Когато вдигате новия клъстер, заедно сложете Calico вместо flannel.

Какво да правите, ако сертификатите ви са издадени за сто години и не планирате да разгръщате отново кластера? Съществува нещо наречено Kube-RBAC-Proxy. Това е страхотна разработка, която позволява да се интегрира като sidecar контейнер във всеки под в Kubernetes кластера. Тя всъщност добавя авторизация чрез RBAC на самия Kubernetes към този под.

Има един проблем. Преди в прометеус оператора това решение Kube-RBAC-Proxy беше вградена. Но после го няма. Сега съвременните версии разчитат на това, че имате мрежова политика и ги затваряте с тях. И затова ще трябва да пренапишете малко чарта. Всъщност, ако влезете в този репозиторий, там има примери как да се използва като sidecars, и чарта ще трябва да се пренапише минимално.

Има и още един малък проблем. Не само Prometheus предоставя своите метрики на когото и да било. Всички компоненти на Kubernetes кластера също могат да предоставят свои метрики.

Но както вече казах, ако не можете да получите достъп до клъстера и да съберете информация, то поне можете да причините щети.

Така че бързо ще покажа два начина, как можете да вредите на здравето на Kubernetes кластера.

Ще се смеете, когато ви го разкажа — това са два случая от реалния живот.

Първият метод. Изчерпване на ресурсите.

Стартираме още един специален под. Той ще има такава секция.

resources: 
    requests: 
        cpu: 4 
        memory: 4Gi 

Както знаете, requests — това е количеството ЦПУ и памет, което се резервира на хоста за конкретни подове с requests. Ако имаме четириядрен хост в Kubernetes кластера и идва под с requests четири по ЦПУ, значи няма да може да дойде повече от един под с requests на този хост.

Ако стартирам такъв под, после направя команда:

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

Тогава повече никой няма да може да се разгръща в Kubernetes кластера. Защото на всички нодове ще свършат requests. И по този начин аз ще спра вашия Kubernetes кластер. Ако го направя вечерта, мога да спра разгръщанията за доста дълго време.

Ако погледнем отново документацията на Kubernetes, ще видим нещо, наречено Limit Range. То установява ресурсите за обектите в клъстера. Можете да напишете YAML обект Limit Range, да го приложите в определени неймспейсове — и по-нататък в този неймспейс можете да кажете, че имате ресурси за подовете по подразбиране, максимални и минимални.

С помощта на такава концепция можем да ограничим потребителите в конкретни продуктови неймспейсове, като им възпрепятстваме да указват на своите подове всякакви ненужни неща. Но за съжаление, дори ако кажете на потребителя, че не може да стартира подове с заявки за повече от един CPU, съществува такава чудесна команда scale, а те могат да правят scale и чрез dashboard.

И оттук произтича вторият начин. Стартираме 11 111 111 111 111 пода. Това е единадесет милиарда. Не е защото съм измислил това число, а защото сам съм го виждал.

Истинска история. Късно вечерта се готвех да напускам офиса. Виждам, че в ъгъла седи група разработчици и нещо нервно правят с лаптопите. Приближавам се до тях и питам: «Какво ви се случи?»

Малко по-рано, около девет вечерта, един от разработчиците се готвел да се прибира вкъщи. И решава: «Сега ще скалирам приложението си до единица». Натиска единица, а интернет малко забавя. Той отново натиска единица, натиска я, натиска Enter. Тук интернет се оживява — и всичко започва да се скалира до това число.

Истината е, че тази история не се е развивала на Kubernetes, а в момента беше Nomad. Завърши със ситуация, в която след час опити да спрем Nomad от постоянните опити да се скалира, Nomad отговори, че няма да спре да се скалира и няма да се занимава с нищо друго. «Уморих се, напускам». И просто се изключи.

Естествено, опитах да направя същото на Kubernetes. Единадесет милиарда пода не зарадваха Kubernetes, той каза: «Не мога. Превишава вътрешните лимити». А 1 000 000 000 пода обаче успя.

В отговор на един милиард, Кубернетес не се справи. Той наистина започна да скалира. Колкото по-напред напредваше процесът, толкова повече време отнемаше за създаването на нови подове. Но все пак, процесът продължаваше. Единствената проблем е, че ако мога да стартирам неограничен брой подове в моето пространство, дори без заявки и лимити, мога да стартирам с определени задачи такова количество подове, че с помощта на тези задачи нодовете започват да се натрупват по памет и CPU. Когато стартирам толкова много подове, информацията от тях трябва да попадне в хранилището, а именно etcd. А когато в него постъпва твърде много информация, хранилището започва да връща информация твърде бавно — и Кубернетес започва да се забавя.

Но има и още един проблем... Както знаете, управляващите елементи на Кубернетес не са единен централно устройство, а са няколко компонента. Там по-специално има контролер-мениджър, scheduler и други. Всички тези компоненти започват да извършват едновременно ненужна безсмислена работа, която с времето започва да отнема все повече и повече време. Контролер-мениджърът ще създава нови подове. Scheduler ще се опитва да намери нова нода за тях. Новите ноди в клъстера ви вероятно скоро ще свършат. Кубернетес клъстера ще започне да работи все по-бавно.

Но реших да отида и по-далеч. Както знаете, в Кубернетес има нещо, което се нарича услуга. По подразбиране в вашите клъстери, вероятно, услугата работи с помощта на IP таблици.

Ако стартирате един милиард подове, например, а след това с помощта на скрипт накарате Кубернетес да създаде нови услуги:

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

На всички ноди в клъстера приблизително едновременно ще се генерират все нови и нови правила за iptables. Всяка услуга ще генерира по един милиард правила за iptables.

Това всичко проверявах на няколко хиляди, до десетина. И проблемът е, че вече на този праг ssh достъпът до нода става доста проблематичен. Защото пакетите, преминавайки през такова количество вериги, започват да се чувстват не много добре.

И това също се решава с помощта на Кубернетес. Съществува такъв обект като Resource quota. Той задава количеството налични ресурси и обекти за namespace в клъстера. Можем да създадем yaml обект във всеки неймспейс на клъстера Кубернетес. С помощта на този обект можем да кажем, че за този неймспейс е отделено определено количество заявки, лимити, и след това можем да кажем, че в този неймспейс е възможно да се създадат 10 услуги и 10 пода. И разработчикът може да работи по своята задача дори вечер. Кубернетес му казва: „Не може да увеличите вашите подове до такова количество, защото надвишава ресурсната квота“. И така, проблемът е решен. Документация тук.

Възниква един проблемен момент свързан с това. Усещате как става трудно да създадете неймспейс в Кубернетес. За да го създадете, трябва да вземете предвид много неща.

Resource quota + Limit Range + RBAC
• Създаваме namespace
• Създаваме limitrange вътре
• Създаваме resourcequota вътре
• Създаваме serviceaccount за CI
• Създаваме rolebinding за CI и потребители
• По желание стартираме нужните служебни подове

По този повод, искам да споделя моите разработки. Съществува нещо, наречено оператор SDK. Това е начин в клъстера Кубернетес да пишете оператора. Можете да пишете оператори с помощта на Ansible.

Първоначално написахме с Ansible, а след това видях, че има оператор SDK и пренаписах Ansible-ролевата функция в оператор. Този оператор позволява да се създаде в клъстера Кубернетес обект, наречен команда. Вътре в командата той позволява да опишем в yaml средата за тази команда. И вътре в средата на командата той позволява да опишем, че отделяме толкова-то ресурси.

Малко облекчение на целия този сложен процес.

И накрая. Какво да правим с всичко това?
Първо. Pod Security Policy — това е добре. И въпреки че нито един от инсталаторите на Кубернетес до момента не ги използва, все пак трябва да ги използвате в своите клъстери.

Network Policy — това не е просто една излишна функция. Това е нещо, което наистина е необходимо в клъстера.

LimitRange/ResourceQuota — време е да ги използваме. Отдавна започнахме да ползваме това и дълго време бях сигурен, че всички го прилагат. Оказа се, че е рядкост.

Освен това, което споменах по време на доклада, има незадокументирани функции, които позволяват атака на клъстера. Скорошно голямо изследване на уязвимостите в Kubernetes.

Някои неща са толкова тъжни и обидни. Както например, при определени условия, kubelet-ите в клъстера Kubernetes могат да предоставят съдържанието на директорията на warlocks на неавторизирани потребители.

Тук има инструкции как да се възпроизведе всичко, за което говорих. Там се намират файлове с примери от продукция, как изглеждат ResourceQuota и Pod Security Policy. И всичко това може да се пипне.

Благодаря на всички.

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster