Distroless контейнеры — это контейнеры, содержащие только нужные для работы приложения файлы. Из контейнера убираются не используемые программой файлы дистрибутива с целью уменьшить его размер и снизить площадь атаки. Вместо сотен или тысяч ненужных файлов дистрибутива остаются лишь файлы, требуемые для работы. Этот подход изначально был предложен Google.
Если у читателя есть опыт сборки контейнеров он скорее всего сталкивался с “контейнером” scratch. Достаточно написать FROM scratch в Dockerfile и мы получим абсолютно пустую файловую систему и сможем запустить свою программу без файлов вообще. Чтобы работать без системы вообще программа должна быть полностью статически собрана. Она должна быть написана на языке, допускающем такую сборку. Например go, c, c++, rust. Некоторые функции операционной системы в такой сборке не будут работать. Например: нельзя будет сменить пользователя (нет /etc/passwd), использовать локальное время (нет timezone), использовать локаль (нет файлов локали) и даже нельзя будет сделать https запрос (не установлены сертификаты).
На языке допускающим статическую сборку, ради размера image, можно все вышеперечисленное сделать руками. Давайте взглянем, что уже сделано и возможно бесплатно облегчим ваши контейнеры на десяток мегабайт.
Google Distroless Container
Этот набор контейнеров появился раньше всех. Находится он тут. Он содержит контейнеры базирующиеся на debian. В набор входят сборки для статически линкуемых языков, а также для java, python 3 и nodejs. Есть примеры для go и rust. На базе этого набора собираются стандартные контейнеры для kubernetes по этому все проверено и надежно.
Chainguard Images
Chainguard собирает небольшие и безопасные контейнеры. Репозиторий тут. Как базу они используют apko и melange на котором собирается дистрибутив alpine. Основные контейнеры — это сборки c динамическими glibc и musl. Сборок с поддержкой различных runtime гораздо больше, чем в предыдущем проекте. Тут из интересного есть: php и jenkins. Не все имиджи находятся в состоянии stable.
Chiseled Ubuntu
Canonical делает для Microsoft специальные имиджи для .net. Утилита chisel убирает с релизного имиджа Ubuntu все ненужное. Если вы используете .net можете попробовать использовать эти контейнеры как основу сборку вашего проекта. На “.net blog” есть подробный пост про это. Имиджей для других платформ в собранном виде я не нашел.
Slimtoolkit
Утилита https://slimtoolkit.org реализует иной подход. Разработчик строит обычный контейнер на базе удобного ему дистрибутива. Утилита запускает его и запоминает какие файлы и системные вызовы были использованы. После завершения запуска она копирует все затронутые файлы в новый контейнер, строит профили безопасности для apparmor и seccomp а также записывает все использованные сертификаты. В документации утверждается, что такой подход сокращает имиджи в 20 с лишним раз.
Во время моих тестов уменьшение имиджа было куда скромнее в 5-6 раз. Также довольно неудобно, при использовании slim придется всегда собирать только ей и всегда запускать свой контейнер. Есть вариант в докере и в виде сервиса, но все равно это не очень удобно.
Будут ли проблемы при использовании distroless контейнеров? Да! Я столкнулся как минимум с двумя:
Сложно отлаживать. Нельзя просто зайти в shell на контейнере (на pod) и выполнить команды. Не конфиг посмотреть! Даже ps не сделать.
Не все программы готовы работать в таком спартанском окружении. Одна система упорно пыталась открыть файл в /var/log хотя и ничего туда не писала. Slim, однако почему-то это игнорировал и /var/log в результирующий image не включал. Пришлось добавлять руками.
Заключение
Стоит ли использовать distroless контейнеры? Думаю да! Как минимум можно попробовать собраться с новым базовым имиджем и протестировать сборку ваших существующих контейнеров. В случае .net работоспособность гарантируется самим Microsoft.
Если вы используете nodejs, python или java возьмите контейнер с готовым runtime, например от chainguard и попробуйте пересобрать существующие Dockerfile. Велика вероятность, что все заработает из коробки.
Комментарии (15)
kozlyuk
17.01.2023 21:26+8Сложно отлаживать. Нельзя просто зайти в shell на контейнере (на pod) и выполнить команды. Не конфиг посмотреть! Даже ps не сделать.
Достаточно получить PID процесса в контейнере, и в его пространствах имен можно запускать программы с хоста. Например, для Docker:
sudo nsenter -p -t $(docker inspect -f '{{ .State.Pid }}' $container) ps sudo nsenter -n -t $(docker inspect -f '{{ .State.Pid }}' $container) ss -tnlp
Файлы контейнера доступны через /proc/$PID/root. Не очень актуально для distroless, но в обычных контейнерах так удобно редактировать файлы.
Gorthauer87
17.01.2023 23:34А ещё есть такая штука как Nix, с его помощью можно очень даже компактные контейнеры делать, причем с помощью кросскомпиляции, что довольно удобно, особенно на макоси.
kozlyuk
18.01.2023 13:18Подéлитесь удачным опытом?
Если делать контейнер на базе NixOS, он получается не меньше и не факт, что безопаснее. Во-первых,
пакетыдеривации в nixpkgs не принято разбивать. Во-вторых, хотя в PATH не будет шелла, если его не попросить, он может быть подтянут в контейнер ради скрипта-обертки, которые в nixpkgs повсюду. Например, образ с clang и LLVM на базе Debian у меня получался около 900 МБ (и можно было чуть ужать), а с NixOS выходило около 1,5 ГБ. Можно было бы пересобирать зависимости, чтобы уменьшить размер, но это трудозатраты.Gorthauer87
20.01.2023 13:27+1А я придумал как хитрить: ты собираешь nix-shell, в нем билдишь бинарь, а потом через nix-build вызываешь сборку образа, где указываешь прямую ссылку на получившийся бинарь и указываешь только самый минимальный набор зависимостей.
Вот у меня есть оверлей с багфиксами кросс-сборки и там есть пример, но немного корявый.
https://github.com/alekseysidorov/nixpkgs-cross-overlay/tree/main/examples/hello_world
Я лично думаю как найду время, оформлю это все красиво и поделюсь в виде статьи и руководства как этим пользоваться.А еще там во многих деривациях таки есть разделение, ну типа openssl.dev, openssl.bin, openssl.lib.
HappyGroundhog
18.01.2023 10:00Тем не менее, Distroless контейнеры далеко не всегда так безопасны, как кажутся. https://www.form3.tech/engineering/content/exploiting-distroless-images
Вот статья о методиках взлома контейнера от Google. Сам гугл не признает это уязвимостью и исправлять это не будет...
В двух словах, через openssl внутри контейнера можно подгружать любые пользовательские либы, которые будут выполнять код, недоступный вам изначально. В том числе и чтение нужных секретов из контейнера.
antonkh Автор
18.01.2023 10:07Да! Есть довольно много критики distroless именно в области безопасности. Скажем так нельзя считать их безопасными просто по факту.
mogaika
18.01.2023 14:07+2Ссылка с методиками взлома контейнера для меня выглядит высосанной из пальца. Может я что не понимаю, но для доступа к openssl уже должна быть RCE в приложении или утечка доступа к exec контейнера - как при таких проблемах сможет помочь способ сборки image. На этот момент песенка уже спета, разве нет? Все что можно сделать - усложнить поиск payload, что и делает distroless подход
NealOliver60
18.01.2023 15:24+2Сложно отлаживать. Нельзя просто зайти в shell на контейнере (на pod) и выполнить команды. Не конфиг посмотреть! Даже ps не сделать.
В версии кубера 1.25 выкатили - Ephemeral Containers. Как раз для дебага distroless based контейнеров в поде, с монтированием файловой системы etc etc. В ранних версиях куба, (1.23 проверял) можно активировать как future-feature.
Kirikekeks
18.01.2023 20:54-1Знать, что ты Совершенство, знать, что ты Идеал? Может просто обернуться к lxc?
Ogoun
19.01.2023 20:39Если говорить о .net, какие преимущества от контейнера можно получить в сравнении с обычным приложением опубликованным как self-hosted? В ряде случаев я могу понять удобство контейнеров (приложения на python или чем то подобном где так и не смогли победить проблему dll hell, а точнее package hell, или предподготовленные сборки со сложной конфигурацией). Но в случае .net, где нет проблем с версионностью библиотек, пока не могу найти ни одного плюса от них. По тестам, контейнеры еще и дают 2-5% падение производительности.
Чтобы четче обозначить, я сравниваю подход когда какой то инструмент публикует .net приложение просто в каталог и устанавливает в качестве службы, и подход когда это же приложение будет развернуто в контейнере. Интересует реальный профит который можно от этого получить.
easty
19.01.2023 21:46+1Я думаю основной плюс контейнеризации это их оркестрация, разве нет? Сварм Кубер etc всё это очень удобно. CI/CD опять же проще. Сервис же он не один .net приложение, с ним может поставляться куча инфраструктуры
ros0x5Ft
Distroless контенеры
- поправьте заголовок пожалуйстаantonkh Автор
Спасибо!