Posted on 2. May 2024

Захистіть збірку контейнера та публікуйте за допомогою .NET 8

Read this article in your language IT | EN | DE | ES

Захистіть збірку контейнера та публікуйте за допомогою .NET 8


.NET 8 піднімає планку безпеки контейнерів для образів контейнерів .NET та інструментів SDK. За замовчуванням SDK створює образи додатків, які відповідають найкращим галузевим практикам і стандартам. Ми також пропонуємо додаткове посилення безпеки за допомогою образів Chiseled для додаткового рівня захисту.


dotnet publish створить для вас образ контейнера і за замовчуванням налаштує його як non-root. За допомогою .NET дуже просто швидко покращити безпеку ваших виробничих додатків.


У цій статті ви дізнаєтеся, як це зробити:

  • Створення образів non-root контейнерів

  • Налаштуйте Kubernetes, щоб вимагати non-root образи

  • Перевірка образів і контейнерів

  • Використання root  (або інших користувачів)


Ця стаття є продовженням статті Оптимізуйте збірку та публікацію контейнерів за допомогою .NET 8, опублікованої раніше цього місяця. Вона базується на статтях Захистіть свої хмарні додатки .NET за допомогою безкореневих Linux Containers та Запуск non-root контейнерів .NET за допомогою Kubernetes, опублікованих минулого року.


Модель загроз 

Будь-яку розмову про безпеку варто починати з чіткого уявлення про існуючі загрози.


Існує дві основні загрози, які слід враховувати:



Незважаючи на те, що Docker не продається як програмне забезпечення для пісочниці, його налаштування за замовчуванням призначене для захисту ресурсів хоста від доступу до них процесів всередині контейнера.


Це чудовий опис ситуації, від втечі з контейнерів Docker та Kubernetes до root на хості (з посиланням на CVE-2019-5736). Автор говорить, що ми всі разом дуже покладаємося на «налаштування за замовчуванням» різних контейнерних рішень, які ми використовуємо, маючи на увазі, що прорив контейнера є реальною загрозою.


З тієї ж публікації, в розділі «Mitigations»:


Використовуйте низькопривілейованого користувача всередині контейнера


Тут автор фактично говорить про те, що вам потрібно зробити свій внесок, щоб безпечніше покладатися на псевдо-пісочницю контейнерних рішень. Якщо ви цього не зробите, і буде виявлено чергову вразливість контейнерів, то частина тягаря ляже на розробників, які розміщують свої додатки з правами root. Інакше кажучи, «caveat emptor».


У ландшафті безпеки та вразливостей може бути важко орієнтуватися навіть у найкращі часи. Актуалізація залежностей - це перший і найважливіший спосіб зменшити ці ризики, як для хоста, так і для гостя контейнера. Non-root хостинг є чудовим заходом глибокого захисту, який може захистити від невідомих майбутніх вразливостей.

Контейнерна екосистема: root за замовчуванням

 

Базові образи за замовчуванням налаштовано під користувачем root .

Після того, як я пояснив, що образи слід налаштовувати як non-root як важливий захід безпеки, я демонструю, що більшість базових образів публікуються з правами root, в тому числі і ті, які публікуємо ми. Чому?


Зручність використання переважає над безпекою для базових образів загального призначення і завжди буде переважати. Важливо, щоб керування пакунками та інші привілейовані операції були простими і щоб образи вищих рівнів могли вибирати потрібного користувача.


Однак, залишається вірним, що зловмисник з активною RCE-уразливістю зможе робити все, що завгодно, маючи привілеї root.


На відміну від них, базові образи Ubuntu Chiseled і Chainguard орієнтовані на пристрої і використовують інший підхід, ніж образи загального призначення. Вони обмінюють зручність використання і сумісність на безпеку. Ми підтримуємо цю точку зору.


Примітка: див. Образи захищених контейнерів: Зображення для безпечного ланцюга постачання.


Це багато контексту про базові образи і відмінний перехід до образів додатків, які (на нашу думку) повинні бути побудовані з філософією безпеки в першу чергу.

.NET екосистеми: Non-root за замовчуванням

За замовчуванням dotnet publish створює образи без права доступу до кореневого каталогу. Давайте подивимося на це за допомогою простої консольної програми. Я пропущу ряд кроків, які описано у статті Спрощення збірки контейнера та публікації за допомогою .NET 8.


Це вихідний код програми.

Створити зображення контейнера дуже просто.

 

 

Додаток привітається з користувачем, який запускає процес, як ви можете бачити з вихідного коду.

 

Як і очікувалося, ми бачимо Hello app.


Ми також можемо запустити whoami так само, як це було зроблено з базовими зображеннями.

 

Як видно, цей образ не використовує root, на відміну від базових образів, які ми розглянули.


Запуск whoami вимагає запуску образу. Kubernetes не робить цього; він дивиться на метадані зображення контейнера, щоб визначити користувача.


Давайте подивимося на метадані контейнера.

 


SDK встановлює користувача за допомогою UID, оскільки це вимагається Kubernetes для застосування властивості runAsNonRoot.


Ми можемо зазирнути трохи глибше, щоб побачити, звідки береться значення 1654.

 

 

Ми визначаємо користувача з ім'ям app і даємо йому UID > 1000, щоб уникнути зарезервованих діапазонів. 1654 - це 1000  + ASCII-значення кожного з символів у dotnet. Ми також встановлюємо змінну оточення - APP_UID - з таким самим значенням. Це дозволяє уникнути необхідності запам'ятовувати або використовувати це значення (без змінної оточення) для звичайних сценаріїв.


У попередньому дописі я включив набір цікавих демонстраційних прикладів для користувачів, які не мають права root. Ви можете переглянути цю статтю, щоб дізнатися більше.

Non-root Dockerfiles

Модель з Dockerfiles схожа, але вимагає одного додаткового кроку - встановлення інструкції USER .


Я покажу вам, як це виглядає, використовуючи цей приклад Dockerfile.


Цей Dockerfile використовує змінну оточення, яку ми щойно розглянули, для визначення користувача. Це шаблон, який ми хочемо, щоб усі використовували для переходу до користувача без прав суперкористувача за допомогою Dockerfiles. Знову ж таки, цей шаблон дозволяє уникнути повсюдного використання магічних чисел і найкраще працює у Kubernetes.

Примітка: Багато розробників вже створили власного користувача. Продовження роботи з власним користувачем або перемикання на вбудованого - обидва варіанти є чудовими.


Після цього ми можемо створити і запустити образ.

 

 

Як бачите, програма працює від імені користувача app.

Перемикач для увімкнення non-root  хостингу (у Dockerfiles) - це лише зміна одного рядка.

Ubuntu Chiseled образи

Образи Ubuntu Chiseled схожі на пристрої, що надають заблокований досвід роботи за замовчуванням. Вони сумісні зі звичайною Ubuntu, проте мають гострі краї, де вирізані цілі розділи операційної системи. Примітно, що вони налаштовані як non-root. Це означає, що вам навіть не потрібно налаштовувати користувача, оскільки він вже налаштований.


Ви можете оглянути вирізане зображення, щоб переконатися, що користувач налаштований.

 

 

У нас є інший приклад Dockerfile, який покладається на користувача, встановленого в цих зображеннях.

 

Як ви можете бачити, у цьому Dockerfile не задано USER. Давайте зберемо і запустимо його.

 

Знову ж таки, додаток запускається від імені користувача app. Якщо ви використовуєте чисельні зображення, ви отримуєте кращі результати і менше роботи у вашому Dockerfile.


Ви можете так само легко використовувати Chiseled images за допомогою SDK publish.

 

 

Ця команда створить non-root образ, оскільки наші Chiseled images налаштовані як non-root , а dotnet publish створює non-root образи за замовчуванням.


Kubernetes

Kubernetes має механізм runAsNonRoot, який є частиною стандартів безпеки. Якщо цей параметр встановлено у true, Kubernetes не зможе завантажити маніфест пакунків, якщо образ контейнера має права root.


Я вважаю runAsNonRoot функцією типу «ролі та обов'язки». Роль образу контейнера визначає користувач. Відповідальність оркестратора полягає у перевірці того, що користувач встановлений як очікується, як non-root.


Згадайте погляд на метадані контейнера «як це робиться у Kubernetes», який ми розглядали раніше.

 

Kubernetes не використовує docker inspect, але ідея та сама. Він дивиться на те саме значення User, визначає, чи є це значення UID, і якщо так, то перевіряє value > 0. Якщо цей вираз дорівнює true, то проходить перевірка runAsNonRoot. У контексті root має UID 0, тому ця перевірка є аналогом user != root.


Давайте швидко розглянемо, як працює non-root у Kubernetes. Якщо ви хочете дізнатися більше, читайте статтю Запуск non-root .NET контейнерів у Kubernetes.


Ось приклад того, як задати runAsNonRoot у маніфесті Pod

У цьому прикладі кожен перелічений контейнер (навіть якщо у прикладі лише один) має бути non-root. securityContext також може бути задано для контейнера. Ви можете переглянути ці налаштування у ширшому контексті у файлі non-root.yaml.


Насправді цікаво подивитися, що станеться, якщо runAsNonRoot буде встановлено у true і ми спробуємо завантажити образ, який використовує користувачів  root.


На момент написання статті образ mcr.microsoft.com/dotnet/samples:aspnetapp-chiseled (використаний вище) налаштовано як non-root, а  mcr.microsoft.com/dotnet/samples:aspnetapp - як root. Я зміню значення image в маніфесті на mcr.microsoft.com/dotnet/samples:aspnetapp, а потім подивлюся, чи не відбудеться збій при завантаженні.

Як бачите, навантаження не проходить.

 

Якщо копнути трохи глибше, то можна зрозуміти причину.

Це відповідає очікуванням. Добре.

Змініть користувача на root

Бувають випадки, коли користувачеві потрібно встановити права користувача root. Зробити це дуже просто.


Можна (за допомогою Docker) запустити команду від імені користувача root у запущеному контейнері за допомогою docker exec -u. Найчастіше це буде команда bash, але ми будемо використовувати whoami, оскільки вона пропонує кращу демонстрацію.

 

Зверніть увагу, що kubectl exec не пропонує аргумент -u (з поважної причини).


Аналогічно, контейнер можна запустити від імені певного користувача, перевизначивши користувача, вказаного у метаданих зображення.

 

Нарешті, конкретний користувач може бути використаний при створенні образу за допомогою ContainerUser.

Вказаний ContainerUser  повинен існувати.

 

Однак ви можете використовувати дійсний UID.

 

Як ви можете бачити, у зображеннях контейнерів, які ми публікуємо, визначені як користувач root, так і користувач app.

Закриття

Користувач для виробничих програм є ключовою частиною будь-якого плану безпеки. На жаль, його легко пропустити, оскільки все працює без вказівки користувача. Насправді, все працює занадто добре. Можна сказати, що це і є корінь проблеми.


Додати користувача до Dockerfile дуже просто. Створити наскрізні робочі процеси, які надійно встановлюють бажані результати безпеки, набагато складніше. Як бачите, тепер легко створювати non-root образи контейнерів за допомогою dotnet publish або Dockerfiles. Образи будуть коректно працювати з функціями безпеки Kubernetes, що є критично важливим для забезпечення бажаних політик безпеки.


Завжди будуть потрібні додаткові налаштування безпеки. Non-root хостинг - це одна з найвпливовіших змін, яку ви можете зробити.



Exception: Stack empty.
Comments are closed