Введення в мережеві політики Kubernetes для фахівців з безпеки
Прим. перев.: Автор статті — Reuven Harrison — має понад 20 років досвіду у розробці програмного забезпечення, а на сьогоднішній день є технічним директором та співзасновником компанії Tufin, яка створює рішення для управління політиками безпеки. Розглядаючи мережеві політики Kubernetes як досить потужний засіб для сегментації мережі в кластері, він у той же час вважає, що вони не такі прості у застосуванні на практиці. Даний матеріал (досить об'ємний) покликаний покращити обізнаність фахівців у цьому питанні та допомогти їм у створенні необхідних конфігурацій.
Сьогодні багато компаній все частіше вибирають Kubernetes для запуску своїх програм. Інтерес до цього програмного забезпечення настільки високий, що деякі називають Kubernetes «новою операційною системою для центрів обробки даних». Поступово Kubernetes (або k8s) починає сприйматися як критично важлива частина бізнесу, яка потребує організації зрілих бізнес-процесів, у тому числі забезпечення безпеки мережі.
Для фахівців з безпеки, яких спантеличили роботою з Kubernetes, справжнім відкриттям може стати політика цієї платформи за умовчанням: дозволити все.
Цей посібник допоможе розібратися у внутрішньому пристрої мережевих політик; зрозуміти, чим вони відрізняються від правил для звичайних брандмауерів. Також буде розказано про деякі підводні камені та будуть надані рекомендації, які допоможуть захистити програми в Kubernetes.
Мережеві політики Kubernetes
Механізм мережевих політик Kubernetes дозволяє керувати взаємодією розгорнутих на платформі додатків на мережевому рівні (третій у моделі OSI). Мережеві політики позбавлені деяких передових функцій сучасних брандмауерів, таких як контроль на 7 рівні OSI та виявлення загроз, проте вони забезпечують базовий рівень мережевої безпеки, який є непоганою відправною точкою.
Мережеві політики контролюють комунікації між pod'ами
Робочі навантаження в Kubernetes розподіляються по pod'ах, які складаються з одного або кількох контейнерів, розгорнутих спільно. Kubernetes надає кожному pod'у IP-адресу, доступну з інших pod'ів. Мережеві політики Kubernetes задають права доступу для груп pod'ів так само, як групи безпеки в хмарі використовуються для керування доступом до екземплярів віртуальних машин.
Визначення мережевих політик
Як і інші ресурси Kubernetes, мережні політики задаються мовою YAML. У наведеному нижче прикладі додатку balance відкривається доступ до postgres:
(Прим. перев.: цей скріншот, як і всі наступні аналогічні, створено не рідними засобами Kubernetes, а за допомогою інструменту Tufin Orca, за розробкою якого стоїть компанія автора оригінальної статті і згадується в кінці матеріалу.)
Для визначення власної мережевої політики знадобляться базові знання YAML. Ця мова ґрунтується на відступах (що задаються пробілами, а не табуляцією). Елемент із відступом належить найближчому елементу з відступом над ним. Новий елемент списку починається з дефісу, всі інші елементи мають вигляд ключ-значення.
Описавши політику на YAML, використовуйте кубектл, щоб створити її в кластері:
kubectl create -f policy.yaml
Специфікація мережевої політики
Специфікація мережевої політики Kubernetes включає чотири елементи:
podSelector: визначає pod'и, що торкаються цієї політики (мети) - обов'язковий;
policyTypes: вказує, які типи політик включені до цієї: ingress та/або egress — необов'язковий, проте я рекомендую його явно прописувати у всіх випадках;
ingress: визначає дозволений що входить трафік у цільові pod'и – необов'язковий;
egress: визначає дозволений вихідний трафік із цільових pod'ів — необов'язковий.
Приклад, запозичений із сайту Kubernetes (я замінив role на app), показує, як використовуються всі чотири елементи:
Зауважте, що всі чотири елементи включати необов'язково. Обов'язковим є лише podSelectorІнші параметри можна використовувати за бажанням.
Якщо опустити policyTypes, політика інтерпретуватиметься так:
За умовчанням передбачається, що вона визначає ingress-сторону. Якщо явних вказівок щодо цього у політиці не міститься, система вважатиме, що весь трафік заборонено.
Поведінка на egress-стороні визначатиметься наявністю або відсутністю відповідного egress-параметра.
Щоб уникнути помилок, я рекомендую завжди явно вказувати policyTypes.
Відповідно до наведеної вище логіки у випадку, якщо параметри ingress і / або egress опущені, політика заборонятиме весь трафік (див. «Правило зачистки» нижче).
За замовчуванням — дозволити
Якщо політики не визначені, Kubernetes за промовчанням дозволяє весь трафік. Усі pod'и вільно можуть обмінюватися інформацією між собою. З точки зору безпеки це може здатися нелогічним, але згадайте про те, що Kubernetes спочатку створювався розробниками для забезпечення взаємодії додатків. Мережеві політики було додано пізніше.
Простір імен
Простір імен (Namespaces) - механізм колективної роботи Kubernetes. Вони призначені для ізолювання логічних оточень друг від друга, у своїй обмін даними між просторами за умовчанням дозволено.
Як і більшість компонентів Kubernetes, мережеві політики мешкають у певному просторі імен. У блоці metadata можна прописати, якому саме простору належить політика:
Якщо простір імен у метаданих явно не прописано, система використовуватиме namespace, вказане в kubectl (за замовчуванням namespace=default):
kubectl apply -n my-namespace -f namespace.yaml
Я рекомендую явно вказувати namespace, якщо ви не пишете політику, призначену відразу кількох просторів імен.
Основний елемент podSelector у політиці обиратиме pod'и з простору імен, до якого належить політика (він позбавлений доступу до pod'ів з іншого простору імен).
Аналогічним чином podSelector'и у блоках ingress та egress можуть вибирати pod'и тільки зі свого простору імен, якщо, звичайно, ви не поєднаєте їх за допомогою namespaceSelector (про це йтиметься у розділі «Фільтр за просторами імен та pod'ам»).
Правила іменування політик
Назви політика унікальні в рамках одного простору імен. Двох політик з однаковою назвою в одному просторі не може бути, але можуть бути політики з однаковими назвами в різних просторах. Це зручно, коли ви хочете повторно застосувати ту саму політику на кількох просторах.
Мені особливо подобається один із способів іменування. Він полягає в тому, щоб поєднувати назву простору імен із цільовими pod'ами. Наприклад:
До об'єктів Kubernetes, таких як pod'и та простору імен, можна прикріплювати лейбле користувача. Лейбли (етикетки - Мітки) є еквівалентом тегів у хмарі. Мережеві політики Kubernetes використовують лейбли для вибору pod'ів, до яких вони застосовуються:
podSelector:
matchLabels:
role: db
... або просторів імендо яких вони застосовуються. У цьому прикладі вибираються всі pod'и у просторах імен з відповідними лейблами:
Одна застереження: при використанні namespaceSelectorпереконайтеся, що вибрані простору імен містять у собі потрібний лейбл. Майте на увазі, що вбудовані простори імен, такі як default и kube-system, за замовчуванням не містять лейблів.
Додати лейбл до простору можна так:
kubectl label namespace default namespace=default
При цьому namespace у розділі metadata повинен посилатися на фактичне ім'я простору, а не на лейбл:
Політики для брандмауерів складаються з правил із джерелами та адресатами. Мережеві політики Kubernetes визначаються для мети - набору з pod'ів, до яких вони застосовуються, а потім встановлюють правила для вхідного (ingress) та/або вихідного (egress) трафіку. У нашому прикладі метою політики будуть усі pod'и у просторі імен default з лейблом із ключем app та значенням db:
підрозділ ingress у цій політиці відкриває вхідний трафік до цільових pod'ів. Інакше кажучи, ingress виступає джерелом, а ціль — відповідним адресатом. Аналогічним чином egress є адресатом, а мета його джерелом.
Це еквівалентно двом правилам для брандмауера: Ingress → Мета; Мета → Egress.
Egress та DNS (важливо!)
Обмежуючи вихідний трафік, особливу увагу зверніть на DNS — Kubernetes використовує цю службу зіставлення сервісів з IP-адресами. Наприклад, наступна політика не спрацює, оскільки ви не дозволили застосуванню balance звертатися до DNS:
Останній елемент to — порожній, і тому він опосередковано вибирає всі pod'и у всіх просторах імендозволяючи balance посилати DNS-запити до відповідної служби Kubernetes (зазвичай вона працює у просторі kube-system).
Цей підхід працює, проте він надмірно вирішальний та небезпечний, оскільки дозволяє спрямовувати DNS-запити за межі кластера.
Поліпшити його можна трьома послідовними кроками.
1. Дозволити DNS-запити тільки всередині кластера, додавши namespaceSelector:
2. Дозволити DNS-запити лише у просторі імен kube-system.
Для цього потрібно додати лейбл у простір імен kube-system: kubectl label namespace kube-system namespace=kube-system - І прописати її в політиці за допомогою namespaceSelector:
3. Параноїки можуть піти ще далі і обмежити DNS-запити певною DNS-службою kube-system. У розділі «Фільтр за просторами імен І pod'ам» буде розказано, як цього досягти.
Інший варіант - дозволити DNS лише на рівні простору імен. У цьому випадку його не потрібно буде відкривати для кожної служби:
порожній podSelector вибирає всі pod'и у просторі імен.
Перша відповідність та порядок правил
У звичайних брандмауерах дія ("Дозволити" або "Заборонити") щодо пакета визначається першим правилом, якому він задовольняє. У Kubernetes порядок політик не має жодного значення.
За замовчуванням, коли політики не задані, комунікації між pod'ами дозволені і можуть вільно обмінюватися інформацією. Як тільки ви починаєте формулювати політики, кожен pod, порушений хоча б однією з них, стає ізольованим відповідно до диз'юнкції (логічним АБО) всіх політиків, які його обрали. Pod'и, не порушені будь-якою політикою, залишаються відкритими.
Змінити таку поведінку можна за допомогою правила зачистки.
Правило зачистки («Заборонити»)
Політики брандмауерів зазвичай забороняють будь-який явно не дозволений трафік.
У Kubernetes немає дії "заборонити" (deny), однак аналогічного ефекту можна досягти зі звичайною (дозволяючою) політикою, вибравши порожню групу pod'ів-джерел (ingress):
Врахуйте, що будь-які додаткові політики, що дозволяють трафік до pod'ів у просторі імен, матимуть пріоритет над цим правилом (аналогічно доданню дозволяючого правила перед брандмауера, що забороняє в конфігурації).
Дозволити все (Any-Any-Any-Allow)
Щоб створити політику «Дозволити все», необхідно доповнити наведену вище заборонну політику порожнім елементом ingress:
Вона відкриває доступ зі всіх pod'ів у всіх просторах імен (і всіх IP) до будь-якого pod'у у просторі імен default. Така поведінка включена за умовчанням, тому зазвичай її не потрібно визначати додатково. Однак іноді може знадобитися тимчасово відключити деякі конкретні дозволи для діагностики проблеми.
Правило можна звузити та дозволити доступ тільки до певному набору pod'ів (app:balance) у просторі імен default:
Політики об'єднуються за допомогою логічного АБО на трьох рівнях; дозволи кожного pod'а встановлюються відповідно до диз'юнкції всіх політик, які його зачіпають:
1. У полях from и to можна визначити три типи елементів (усі вони комбінуються за допомогою АБО):
namespaceSelector - Вибирає простір імен цілком;
podSelector - Вибирає pod'и;
ipBlock - Вибирає підсіти.
При цьому кількість елементів (навіть однакових) у підрозділах from/to НЕ обмежено. Усі вони будуть об'єднані логічним АБО.
2. Усередині політики розділ ingress може мати безліч елементів from (Об'єднуються логічним АБО). Аналогічно розділ egress може включати безліч елементів to (також об'єднуються диз'юнкцією):
Але при їх об'єднанні існує одне обмеження, на яке вказавChris Cooney: Kubernetes може комбінувати політики тільки з різними policyTypes (Ingress або Egress). Політики, що визначають ingress (або egress), перезапишуть один одного.
Зв'язок між просторами імен
За промовчанням обмін інформацією між просторами імен дозволено. Змінити це можна за допомогою заборонної політики, яка обмежить вихідний та/або вхідний трафік у простір імен (див. «Правило зачистки» вище).
Заблокувавши доступ до простору імен (див. «Правило зачистки» вище), ви можете внести винятки в заборонну політику, дозволивши підключення з певного простору імен за допомогою namespaceSelector:
В результаті всі pod'и у просторі імен default отримають доступ до pod'ів postgres у просторі імен database. Але що, якщо ви хочете відкрити доступ до postgres тільки конкретним pod'ам у просторі імен default?
Фільтр за просторами імен І pod'ам
Kubernetes версії 1.11 та вище дозволяє комбінувати оператори namespaceSelector и podSelector за допомогою логічного І. Виглядає це так:
Зверніть увагу, що podSelector не починається з дефісу. У YAML це означає, що podSelector і стоїть перед ним namespaceSelector відносяться до того самого елемента списку. Тому вони поєднуються логічним І.І.
Додавання дефісу перед podSelector призведе до виникнення нового елемента списку, який комбінуватиметься з попереднім namespaceSelector за допомогою логічного АБО.
Щоб вибрати pod'и з певним лейблом у всіх просторах імен, впишіть порожній namespaceSelector:
Правила для брандмауера з багатьма об'єктами (хостами, мережами, групами) комбінуються за допомогою логічного АБО. Наступне правило спрацює, якщо джерело пакету збігається з Host_1 АБО Host_2:
Навпаки, у Kubernetes різні лейбли в podSelector або namespaceSelector поєднуються логічним І. Наприклад, наступне правило вибере pod'и, що володіють обома лейблами, role=db И version=v2:
podSelector:
matchLabels:
role: db
version: v2
Та ж логіка застосовується до всіх типів операторів: селекторів цілей політики, селекторів pod'ів та селекторів просторів імен.
Підмережі та IP-адреси (IPBlocks)
Для сегментування мережі брандмауери використовують VLAN, IP-адреси та підмережі.
У Kubernetes IP-адреси присвоюються pod'ам автоматично і можуть часто змінюватися, тому для вибору pod'ів і просторів імен у мережевих політиках використовуються лейбли.
Підмережі (ipBlocks) використовуються при керуванні вхідними (ingress) або вихідними (egress) зовнішніми (North-South) підключеннями. Наприклад, ця політика відкриває всім pod'ам з простору імен default доступ до DNS-сервісу Google:
Порожній селектор pod'ів у цьому прикладі означає «вибрати всі pod'и у просторі імен».
Ця політика відкриває доступ лише до 8.8.8.8; доступ до будь-якого іншого IP заборонено. Таким чином, ви заблокували доступ до внутрішньої служби DNS Kubernetes. Якщо ви все ж таки хочете його відкрити, вкажіть це явно.
Зазвичай ipBlocks и podSelectors є взаємовиключними, оскільки внутрішні IP-адреси pod'ів не використовуються в ipBlocks. Вказавши внутрішні IP pod'ів, Ви фактично дозволите підключення до/від pod'ів з цими адресами. На практиці ви не знатимете, яку IP-адресу використовувати, саме тому їх не варто застосовувати для вибору pod'ів.
Як контр-приклад наступна політика включає всі IP і, отже, дозволяє доступ до всіх інших pod'ам:
Зазвичай pod'и слухають один порт. Це означає, що можна просто не вказувати номери портів у політиках та залишити все за замовчуванням. Втім, політики рекомендується робити максимально обмежувальними, тому в деяких випадках все ж таки можна вказувати порти:
Зауважте, що селектор ports застосовується до всіх елементів у блоці to або from, В якому міститься. Щоб вказати різні порти для різних наборів елементів, розбийте ingress або egress на кілька підрозділів з to або from і в кожному пропишіть свої порти:
Якщо ви повністю опускаєте визначення портів (ports), це означає всі протоколи та всі порти;
Якщо ви опускаєте визначення протоколу (protocol), це означає TCP;
Якщо ви опускаєте визначення порту (port), це означає всі порти.
Найкраща практика: не покладайтеся на значення за промовчанням, вказуйте потрібне вам явно.
Зверніть увагу, що необхідно використовувати порти pod'ів, а не сервісів (докладніше про це у наступному параграфі).
Політики визначені для pod'ів чи сервісів?
Зазвичай pod'и в Kubernetes звертаються один до одного через сервіс - віртуальний балансувальник навантаження, що перенаправляє трафік до pod'ів, що реалізує сервіс. Можна подумати, що мережеві політики контролюють доступом до сервісів, але це негаразд. Мережеві політики Kubernetes працюють із портами pod'ів, а не сервісів.
Наприклад, якщо сервіс слухає 80-й порт, але перенаправляє трафік на порт 8080 своїх pod'ів, у мережевій політиці необхідно вказати саме 8080.
Подібний механізм слід визнати неоптимальним: за зміни внутрішнього пристрою сервісу (порти якого слухають pod'и) доведеться оновлювати мережеві політики.
Новий архітектурний підхід із використанням Service Mesh (наприклад, див. про Istio нижче - прим. перекл.) дозволяє впоратися із цією проблемою.
Чи потрібно прописувати як Ingress, так і Egress?
Коротка відповідь - так, щоб pod А міг зв'язатися з pod'ом В, необхідно дозволити йому створювати вихідне з'єднання (для цього слід налаштувати egress-політику), а pod повинен мати можливість приймати вхідне з'єднання (для цього, відповідно, потрібна ingress- політика).
Однак на практиці можна покластися на політику за умовчанням, що дозволяє з'єднання в одному або обох напрямках.
Якщо якийсь pod-джерело буде обрано однією або декількома вихід-Політиками, що накладаються на нього обмеження будуть визначатися їх диз'юнкцією. У цьому випадку потрібно явно дозволити підключення до pod'у-адресату. Якщо pod не вибрано будь-якої політики, його вихідний (egress) трафік дозволено за замовчуванням.
Аналогічно доля pod'а-адресата, обраного однією або декількома проникнення-Політиками, визначатиметься їх диз'юнкцією. В цьому випадку необхідно явно дозволити йому отримувати трафік від pod'а-джерела. Якщо pod не вибраний будь-якою політикою, весь вхідний трафік для нього дозволений за замовчуванням.
Див. «Stateful або Stateless» нижче.
Список
Мережеві політики Kubernetes не можуть журналювати трафік. Це ускладнює визначення того, чи працює політика належним чином, і сильно ускладнює аналіз безпеки.
Контроль за трафіком до зовнішніх сервісів
Мережеві політики Kubernetes не дозволяють вказувати повноцінне доменне ім'я (DNS) у розділах egress. Цей факт призводить до значної незручності при спробі обмежити трафік до зовнішніх адресатів, позбавлених фіксованої IP-адреси (наприклад, aws.com).
Перевірка політики
Брандмауери попередять вас або навіть відмовляться ухвалити помилкову політику. Kubernetes також проводить певну верифікацію. При заданні мережевої політики через Kubectl Kubernetes може заявити, що вона неправильна, і відмовитися її прийняти. В інших випадках Kubernetes прийме політику і доповнить її деталями, що бракують. Їх можна побачити за допомогою команди:
kubernetes get networkpolicy <policy-name> -o yaml
Майте на увазі, що система перевірки Kubernetes є непохитною і може пропускати деякі типи помилок.
Виконання
Kubernetes не займається реалізацією мережевих політик самостійно, а є лише API-шлюзом, що покладає обтяжливу роботу з контролю на систему, що нижче лежить, звану Container Networking Interface (CNI). Завдання політик у кластері Kubernetes без призначення відповідного CNI аналогічно до створення політик на сервері управління брандмауером без їх подальшої установки в брандмауери. Ви самі повинні переконатися у наявності гідного CNI або, у випадку платформ Kubernetes, розміщених у хмарі (Зі списком провайдерів можна ознайомитися тут - прим. пров.), задіяти мережеві політики, які встановлять CNI для вас.
Зверніть увагу, що Kubernetes не попередить вас, якщо ви поставите мережну політику без відповідного допоміжного CNI.
Stateful чи Stateless?
Всі CNI Kubernetes, з якими мені доводилося зіштовхуватися, зберігають стани (наприклад, Calico використовує Linux conntrack). Це дозволяє pod'у отримувати відповіді щодо ініційованого ним TCP-з'єднання без необхідності встановлювати його заново. При цьому мені невідомо про стандарт Kubernetes, який би гарантував зберігання стану (statefulness).
Просунуте управління політикою безпеки
Ось кілька способів підвищити ефективність виконання політики безпеки в Kubernetes:
Архітектурний патерн Service Mesh використовує sidecar-контейнери для забезпечення детальної телеметрії та контролю за трафіком на рівні сервісів. Як приклад можна взяти Істіо.
Деякі з постачальників CNI доповнили свої інструменти, щоб ті вийшли за межі мережевих політик Kubernetes.
Tufin Orca забезпечує прозорість та автоматизацію мережевих політик Kubernetes.
Пакет Tufin Orca керує мережевими політиками Kubernetes (і є джерелом скріншотів, наведених вище).
Мережеві політики Kubernetes пропонують непоганий набір інструментів для сегментації кластерів, проте вони інтуїтивно незрозумілі та мають безліч тонкощів. Я вважаю, що через цю складність політики багатьох кластерів містять помилки. Можливими рішеннями цієї проблеми є автоматизація визначень політик чи застосування інших засобів сегментації.
Сподіваюся, що цей посібник допоможе прояснити деякі питання та вирішити проблеми, з якими ви можете зіткнутися.