Безпека Docker – Безпечна конфігурація (Частина 1)

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

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

Розпочнемо

Припускаємо, що ви знайомі з основами роботи Docker. Це означає, що ви повинні розуміти його принципи, знати різницю між контейнером і зображенням , а також знати, як завантажити зображення зі сховища за допомогою команди docker pullта як запустити контейнер за допомогою docker runкоманди. Також важливо, щоб ви мали уявлення про використання та застосування Dockerfile .

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

Безпечна конфігурація

Безпечна конфігурація – це набір налаштувань, який мінімізує ризик інцидентів із програмним забезпеченням, яке ви використовуєте. Також важливо внести вдосконалення, де це можливо, щоб підвищити рівень безпеки порівняно з конфігурацією за замовчуванням, отриманою «з коробки».

Малюнок 1. Архітектура платформи Docker (власна робота на основі https://docs.docker.com/get-started/overview/ )

У контексті Docker ми зосередимося на захисті ключових елементів платформи, як показано на малюнку 1:

  • Хост Docker – це машина (система), на якій Docker запускає контейнери. Безпека контейнерів тісно пов’язана з рівнем безпеки Docker Host. Рекомендується, щоб ця машина не виконувала жодних додаткових ролей, що допомагає мінімізувати поверхню атаки. Також вигідно використовувати образи операційних систем без зайвих пакетів (так звані мінімальні образи),

  • Docker Daemon – це сервіс, який відповідає за керування контейнерами Docker. Він відстежує та керує зображеннями, контейнерами, мережами та томами. Це також полегшує спілкування з іншими демонами Docker. Як правило, Docker Daemon працює на тій же машині, що й Docker Host, але це не правило, оскільки Docker дозволяє запускати їх на окремих машинах,

  • Зображення та контейнери – образи Docker – це шаблони, які містять програмне забезпечення та конфігурацію, необхідні для запуску програм. Контейнери Docker — це запущені екземпляри зображень. Їх можна порівняти з віртуальними машинами, але вони легші (вони не містять системного рівня). Контейнер містить усе необхідне для запуску програми, забезпечуючи сумісність у різних середовищах і платформах.

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

Докер-хост

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

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

Робоче середовище

Під час створення статті та підготовки прикладів ми спиралися на середовище Docker, яке запускалося на Ubuntu Server 22.04 LTS . Це не випадковий вибір – у багатьох місцях творці Docker рекомендують використовувати саме цей дистрибутив, а точніше системне ядро, яке постачається разом із Ubuntu (мал. 2).

Малюнок 2. Рекомендації щодо використання системи на Docker Host.

Варто зазначити, що супроводжуючі дистрибутиви, такі як Ubuntu або Fedora, надають «неофіційні» версії інсталяційних пакетів, як це визначено командою Docker. Прикладом може бути docker.ioпакет, який часто згадується в різних посібниках. Його можна встановити за допомогою простої aptкоманди (лістинг 1).

sudo apt update && sudo apt install -y docker.io

Лістинг 1. Встановлення пакета docker.io.

Однак, згідно з рекомендаціями в документації Docker, пропонуемо використовувати офіційний інсталяційний пакет. Процес встановлення детально описано в документації, а в лістингу 2 ви знайдете інструкції, актуальні на момент написання цієї статті. Для цього потрібно виконати декілька команд у консолі (мал. 3).

sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyring
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Лістинг 2. Встановлення Docker з офіційного репозиторію.

Подвійне клацання будь-якого code blockкопіює його вміст у буфер обміну!

Малюнок3. Встановлення Docker з офіційного репозиторію.

dockerdЩоб перевірити, чи правильно встановлено та запущено демон Docker ( ), ви можете скористатися командами docker -vта/або service docker status(Малюнок 4).

Малюнок 4. Перевірка правильності встановлення Docker.

Аудит конфігурації

Як зазначалося раніше, безпека хосту Docker значною мірою залежить від конфігурації операційної системи, у якій він встановлений. Термін «система» тут стосується операційної системи, що працює на вибраному сервері, наприклад Ubuntu, Debian, RHEL тощо. Таким чином, ми зосереджуємося на типовій безпеці системи Linux, беручи до уваги специфічну взаємодію між хостом Docker і Docker Демон.

Lynis

Я рекомендую захистити систему, яка виконує функцію Docker Host, дотримуючись інструкцій, наданих інструментом Lynis . Lynis — це безкоштовний інструмент (скрипт), також доступний для комерційного використання, ліцензований під GPLv3, який запускається на машині, що проходить аудит. Він сумісний з багатьма популярними системами, включаючи ті, що базуються на ядрах Linux, macOS і BSD.

Важливо, що Lynis містить вбудовані функції, які – окрім аналізу конфігурації операційної системи – також враховують параметри, характерні для систем, де запущено Docker Daemon. Детальну інформацію про це можна знайти в репозиторії Lynis у файлі під назвою include/tests_containers.

Інструмент Lynis доступний у репозиторіях популярних дистрибутивів, включаючи Ubuntu, тому ви можете встановити його за допомогою команди sudo apt install lynis(мал. 5).

Малюнок 5. Встановлення інструменту Lynis.

Репозиторій пакетів вибраного дистрибутива зазвичай містить версію програмного забезпечення, яка на одну чи дві редакції старша за ту, яка доступна в офіційному репозиторії GitHub. Якщо ви хочете використовувати останню версію, у вас є така можливість, запустивши код безпосередньо з репозиторію GitHub (лістинг 3).

git clone --quiet https://github.com/CISOfy/lynis
cd lynis
./lynis audit system

Лістинг 3. Завантаження та запуск Lynis безпосередньо зі сховища.

Після завантаження Lynis готовий до використання.

Малюнок 6. Запуск першого сканування за допомогою інструменту Lynis.

Lynis можна запускати з непривілейованими дозволами користувача, але деякі тести можуть бути неповними або надавати неточну інформацію (Малюнок 6). Рекомендовано запускати Lynis з правами адміністратора за допомогою sudoкоманди (лістинг 4).

sudo lynis audit system

Лістинг 4. Запуск Lynis за допомогою sudo.

Слід зазначити, що Lynis, окрім визначення областей, які потребують вдосконалення, також надає поради щодо вирішення кожної виявленої проблеми. Наприклад, після аналізу системи програма дає конкретні рекомендації щодо зміни конфігурації, а також посилання на документацію, де окремі теми обговорюються більш детально (мал. 7).

Малюнок 7. Рекомендації, надані Lynis.
Малюнок 8. Зразок рекомендації. Джерело: https://cisofy.com/lynis/controls/SSH-7412/

Лавка Docker для безпеки

Іншим інструментом, який можна використовувати для захисту Docker Host, є Docker Bench for Security . Він заснований на стандарті CIS Docker Benchmark v1.6.0.

Як і належить рішенню для контейнерів, Docker Bench for Security можна запускати як сам контейнер (лістинг 5).

docker run -it --net host --pid host --userns host --cap-add audit_control \
    -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
    -v /var/lib:/var/lib \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /usr/lib/systemd:/usr/lib/systemd \
    -v /etc:/etc --label docker_bench_security \
    docker/docker-bench-security

Лістинг 5. Завантаження та запуск Docker Bench for Security.

Через кілька секунд почнеться процес аудиту конфігурації (Малюнок 9).

Малюнок 9. Запуск контейнера Docker Bench for Security і виконання сканування.

На малюнку 10 представлені додаткові результати та рекомендації, отримані Docker Bench for Security.

Малюнок 10. Додаткові результати та рекомендації, надані Docker Bench for Security.

Деякі моменти, як-от 2.1 і 2.8 (нумерація результатів Docker Bench for Security), будуть обговорюватися в наступних частинах тексту, пов’язаних із підвищенням привілеїв, простором імен Linux та ізоляцією контейнера. Там покажемо, як практично змінити конфігурацію, щоб наступний аудит завершився успішно.

Підсумовуючи, перший крок, який ми маємо зробити, це захистити конфігурацію системи, що виконує роль хоста Docker і демона Docker, звичайно, в межах наших можливостей. Інструкції, надані Lynis і Docker Bench for Security, будуть дуже корисними.

Докер Демон

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

Контроль доступу до Docker Daemon

Ми вже встановили Docker у нашій системі. Отже, давайте перевіримо, чи можемо ми відобразити запущені контейнери за допомогою команди docker ps(мал. 11) або запустити наш перший контейнер ( docker run ubuntu:22.04).

Малюнок 11. Спроба запустити команду docker ps.

На жаль, ми бачимо помилку через те, що клієнт Docker не має доступу до Docker Daemon. Причиною цього є той факт, що лише користувачі, які є адміністраторами ( root) і ті, хто належить до dockerгрупи, мають дозволи на цей сокет ( ls -la /var/run/docker.sock; малюнок 12).

Малюнок 12. Перевірка доступу до сокета Docker Daemon.

Однак наш користувач Reynard не є членом цієї групи за умовчанням. Ми можемо перевірити це за допомогою groupsкоманди (мал. 13)

Малюнок 13. Перевірка членства користувача в групах.

Щоб додати користувача до dockerгрупи, потрібно виконати команду sudo usermod -aG docker reynard. З цього моменту користувач reynardтакож стане членом dockerгрупи та отримає доступ до Docker Daemon (Малюнок 14).

Малюнок 14. Додавання користувача reynard до групи докерів.

Щоб перевірити поточний список користувачів, які належать до цієї групи, ви можете скористатися командою  grep docker /etc/group. Команда members dockerє альтернативним рішенням. У нашій системі до групи належать лише користувачі з іменами reynardта (мал. 15).testdocker

Малюнок 15. Перевірка користувачів, призначених групі Docker.

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

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

Захист docker.sock

Коли ви використовуєте команди інтерфейсу командного рядка Docker, наприклад або docker, клієнт Docker (Docker CLI) за умовчанням спілкується з демоном Docker через сокет для видачі команд. Кожна клієнтська команда Docker викликає REST API відповідного демона Docker через .docker rundocker ps/var/run/docker.sock

Файл /var/run/docker.sockє сокетом UNIX, який забезпечує зв’язок із демоном Docker. Ця проблема є важливою з точки зору безпеки. Контроль над цим сокетом означає контроль над демоном Docker, який дозволяє керувати контейнерами, зображеннями та іншими елементами Docker. Тому доступ до розетки /var/run/docker.sockслід суворо контролювати. Дозволи на використання цього сокета повинні бути надані лише людям і програмам, яким це дійсно потрібно.

Використовуючи docker.sockDocker REST API, ви маєте повний контроль над Docker Daemon. Прикладом може бути отримання списку запущених контейнерів (малюнок 16, лістинг 6).

curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json | jq .

Лістинг 6. Отримання списку запущених контейнерів.

Малюнок 16. Отримання списку контейнерів.

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

docker ps
# In the curl command, you need to replace the container ID with the correct one.
curl --unix-socket /var/run/docker.sock -XPOST http://localhost/containers/84167e82e8cf83d03d8981f80c36a89b2f9d1575cc4ae602be37b30b6711d34f/stop
docker ps

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

Малюнок 17. Зупинка працюючого контейнера.

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

# THIS IS NOT RECOMMENDED!
docker run -it -v /var/run/docker.sock:/var/run/docker.sock [...]

Лістинг 8.  Приклад небезпечної конфігурації.

# THIS IS NOT RECOMMENDED!
docker run -it -v /:/mnt/ [...]

Лістинг 9. Приклад небезпечної конфігурації.

Перша команда docker run -it -v /var/run/docker.sock:/var/run/docker.sockзапускає новий контейнер Docker. Крім того, ця команда монтує сокет Docker з хоста в контейнер, надаючи можливість керувати демоном Docker, запущеним на хості через цей контейнер. Друга команда docker run -it -v /:/mnt/монтує всю файлову систему хоста до /mnt/каталогу в контейнері, надаючи контейнеру повний доступ до файлової системи хоста.

Обидві команди несуть величезний ризик для безпеки. У разі виявлення вразливості безпеки в додатку, що працює в контейнері, і успішної спроби отримати доступ до оболонки контейнера, зловмисник матиме вільний шлях для контролю над хостом Docker. Можна було навіть піти далі. Завдяки можливості налаштування середовища через REST API за допомогою docker.sock, атака не завжди повинна включати віддалене виконання коду на сервері. Іноді достатньо скористатися такою вразливістю, як SSRF .

Тепер припустімо, що ми використовуємо погано налаштований контейнер Docker (лістинг 10).

docker run -it -v /var/run/docker.sock:/var/run/docker.sock --rm ubuntu:22.04 bash

Лістинг 10. Запуск неправильно налаштованого контейнера.

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

apt-get update > /dev/null
apt-get install -y curl > /dev/null
curl -fsSL https://get.docker.com -o install-docker.sh
sh install-docker.sh > /dev/null 2>&1
docker -v

Лістинг 11. Спроба атаки на контейнер.

Малюнок 18. Запуск контейнера.

Важливо, щоб Docker одразу підключався до стандартного сокета ( docker ps; мал. 19):

Малюнок 19. Запуск команди docker ps.

Давайте тепер спробуємо взяти під контроль хост Docker (лістинг 12, малюнок 20).

# Commands to be executed in the context of a vulnerable container (e.g., using RCE vulnerability or a webshell script).
docker run -it -v /:/mnt ubuntu:22.04 bash
ls /mnt
chroot /mnt
grep reynard /etc/shadow

Лістинг 12. Спроба взяти під контроль контейнер.

Малюнок 20. Спроба підвищення привілеїв.

Ця команда docker run -it -v /:/mnt ubuntu:22.04 bashзапускає новий контейнер Docker на основі образу Ubuntu 22.04 і монтує всю файлову систему хоста до /mntмісця в контейнері, а також відкриває інтерактивну bashоболонку. Під час роботи в цій оболонці введення команди ls /mntвідобразить вміст основного каталогу хосту. Потім команда chroot /mntзмінить основний каталог контейнера на /mnt, фактично перемістивши контекст контейнера у файлову систему хосту Docker. Нарешті, grep reynard /etc/shadowкоманда дозволить вам шукати запис, пов’язаний з користувачем, reynardу /etc/shadowфайлі хосту, який зберігає зашифровані паролі користувачів. Можливість читати цей файл підтверджує, що ми маємо високі дозволи на хості Docker. Підсумовуючи, нам вдалося взяти під контроль Docker Host!

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