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 контейнеров? Да! Я столкнулся как минимум с двумя:

  1. Сложно отлаживать. Нельзя просто зайти в shell на контейнере (на pod) и выполнить команды. Не конфиг посмотреть! Даже ps не сделать.

  2. Не все программы готовы работать в таком спартанском окружении. Одна система упорно пыталась открыть файл в /var/log хотя и ничего туда не писала. Slim, однако почему-то это игнорировал и /var/log в результирующий image не включал. Пришлось добавлять руками.

Заключение

Стоит ли использовать distroless контейнеры? Думаю да! Как минимум можно попробовать собраться с новым базовым имиджем и протестировать сборку ваших существующих контейнеров. В случае .net работоспособность гарантируется самим Microsoft.

Если вы используете nodejs, python или java возьмите контейнер с готовым runtime, например от chainguard и попробуйте пересобрать существующие Dockerfile. Велика вероятность, что все заработает из коробки.

Комментарии (15)


  1. ros0x5Ft
    17.01.2023 20:09
    +1

    Distroless контенеры - поправьте заголовок пожалуйста


    1. antonkh Автор
      17.01.2023 20:11

      Спасибо!


  1. 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, но в обычных контейнерах так удобно редактировать файлы.


    1. antonkh Автор
      18.01.2023 10:09

      Спасибо! Отличная утилита. Я про нее не знал. Посмотрю поподробнее.


  1. Gorthauer87
    17.01.2023 23:34

    А ещё есть такая штука как Nix, с его помощью можно очень даже компактные контейнеры делать, причем с помощью кросскомпиляции, что довольно удобно, особенно на макоси.


    1. kozlyuk
      18.01.2023 13:18

      Подéлитесь удачным опытом?


      Если делать контейнер на базе NixOS, он получается не меньше и не факт, что безопаснее. Во-первых, пакеты деривации в nixpkgs не принято разбивать. Во-вторых, хотя в PATH не будет шелла, если его не попросить, он может быть подтянут в контейнер ради скрипта-обертки, которые в nixpkgs повсюду. Например, образ с clang и LLVM на базе Debian у меня получался около 900 МБ (и можно было чуть ужать), а с NixOS выходило около 1,5 ГБ. Можно было бы пересобирать зависимости, чтобы уменьшить размер, но это трудозатраты.


      1. 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.


  1. HappyGroundhog
    18.01.2023 10:00

    Тем не менее, Distroless контейнеры далеко не всегда так безопасны, как кажутся. https://www.form3.tech/engineering/content/exploiting-distroless-images

    Вот статья о методиках взлома контейнера от Google. Сам гугл не признает это уязвимостью и исправлять это не будет...

    В двух словах, через openssl внутри контейнера можно подгружать любые пользовательские либы, которые будут выполнять код, недоступный вам изначально. В том числе и чтение нужных секретов из контейнера.


    1. antonkh Автор
      18.01.2023 10:07

      Да! Есть довольно много критики distroless именно в области безопасности. Скажем так нельзя считать их безопасными просто по факту.


    1. mogaika
      18.01.2023 14:07
      +2

      Ссылка с методиками взлома контейнера для меня выглядит высосанной из пальца. Может я что не понимаю, но для доступа к openssl уже должна быть RCE в приложении или утечка доступа к exec контейнера - как при таких проблемах сможет помочь способ сборки image. На этот момент песенка уже спета, разве нет? Все что можно сделать - усложнить поиск payload, что и делает distroless подход


  1. NealOliver60
    18.01.2023 15:24
    +2

    Сложно отлаживать. Нельзя просто зайти в shell на контейнере (на pod) и выполнить команды. Не конфиг посмотреть! Даже ps не сделать.

    В версии кубера 1.25 выкатили - Ephemeral Containers. Как раз для дебага distroless based контейнеров в поде, с монтированием файловой системы etc etc. В ранних версиях куба, (1.23 проверял) можно активировать как future-feature.


    1. antonkh Автор
      18.01.2023 15:43

      Дебаг отдельная тема - достойна еще одной статьи ;).


  1. Kirikekeks
    18.01.2023 20:54
    -1

    Знать, что ты Совершенство, знать, что ты Идеал? Может просто обернуться к lxc?


  1. Ogoun
    19.01.2023 20:39

    Если говорить о .net, какие преимущества от контейнера можно получить в сравнении с обычным приложением опубликованным как self-hosted? В ряде случаев я могу понять удобство контейнеров (приложения на python или чем то подобном где так и не смогли победить проблему dll hell, а точнее package hell, или предподготовленные сборки со сложной конфигурацией). Но в случае .net, где нет проблем с версионностью библиотек, пока не могу найти ни одного плюса от них. По тестам, контейнеры еще и дают 2-5% падение производительности.

    Чтобы четче обозначить, я сравниваю подход когда какой то инструмент публикует .net приложение просто в каталог и устанавливает в качестве службы, и подход когда это же приложение будет развернуто в контейнере. Интересует реальный профит который можно от этого получить.


    1. easty
      19.01.2023 21:46
      +1

      Я думаю основной плюс контейнеризации это их оркестрация, разве нет? Сварм Кубер etc всё это очень удобно. CI/CD опять же проще. Сервис же он не один .net приложение, с ним может поставляться куча инфраструктуры