Comments 18
Distroless контенеры
- поправьте заголовок пожалуйста
Сложно отлаживать. Нельзя просто зайти в 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, но в обычных контейнерах так удобно редактировать файлы.
А ещё есть такая штука как Nix, с его помощью можно очень даже компактные контейнеры делать, причем с помощью кросскомпиляции, что довольно удобно, особенно на макоси.
Подéлитесь удачным опытом?
Если делать контейнер на базе NixOS, он получается не меньше и не факт, что безопаснее. Во-первых, пакеты деривации в nixpkgs не принято разбивать. Во-вторых, хотя в PATH не будет шелла, если его не попросить, он может быть подтянут в контейнер ради скрипта-обертки, которые в nixpkgs повсюду. Например, образ с clang и LLVM на базе Debian у меня получался около 900 МБ (и можно было чуть ужать), а с NixOS выходило около 1,5 ГБ. Можно было бы пересобирать зависимости, чтобы уменьшить размер, но это трудозатраты.
А я придумал как хитрить: ты собираешь nix-shell, в нем билдишь бинарь, а потом через nix-build вызываешь сборку образа, где указываешь прямую ссылку на получившийся бинарь и указываешь только самый минимальный набор зависимостей.
Вот у меня есть оверлей с багфиксами кросс-сборки и там есть пример, но немного корявый.
https://github.com/alekseysidorov/nixpkgs-cross-overlay/tree/main/examples/hello_world
Я лично думаю как найду время, оформлю это все красиво и поделюсь в виде статьи и руководства как этим пользоваться.
А еще там во многих деривациях таки есть разделение, ну типа openssl.dev, openssl.bin, openssl.lib.
Спасибо, хороший пример.
Отдельные outputs для dev, bin и lib — это не то же, что разбиение пакетов для минимизации зависимостей. Например, openssl.bin притащит за собой Perl ради скрипта c_rehash, а вот в Alpine скрипт разумно вынесли в другой пакет. Или DPDK, из десятков библиотек которого обычно нужна лишь малая часть, в nixpkgs сделан одной деривацией, а в Debian библиотеки вынесены в свои пакеты. Да, в nix подрихтовать деривации проще, чем пакеты в других дистрибутивах, но все равно надо разбираться в самой деривации и системе сборки, иногда выходит грязь.
Тем не менее, Distroless контейнеры далеко не всегда так безопасны, как кажутся. https://www.form3.tech/engineering/content/exploiting-distroless-images
Вот статья о методиках взлома контейнера от Google. Сам гугл не признает это уязвимостью и исправлять это не будет...
В двух словах, через openssl внутри контейнера можно подгружать любые пользовательские либы, которые будут выполнять код, недоступный вам изначально. В том числе и чтение нужных секретов из контейнера.
Да! Есть довольно много критики distroless именно в области безопасности. Скажем так нельзя считать их безопасными просто по факту.
Ссылка с методиками взлома контейнера для меня выглядит высосанной из пальца. Может я что не понимаю, но для доступа к openssl уже должна быть RCE в приложении или утечка доступа к exec контейнера - как при таких проблемах сможет помочь способ сборки image. На этот момент песенка уже спета, разве нет? Все что можно сделать - усложнить поиск payload, что и делает distroless подход
Сложно отлаживать. Нельзя просто зайти в shell на контейнере (на pod) и выполнить команды. Не конфиг посмотреть! Даже ps не сделать.
В версии кубера 1.25 выкатили - Ephemeral Containers. Как раз для дебага distroless based контейнеров в поде, с монтированием файловой системы etc etc. В ранних версиях куба, (1.23 проверял) можно активировать как future-feature.
Знать, что ты Совершенство, знать, что ты Идеал? Может просто обернуться к lxc?
Если говорить о .net, какие преимущества от контейнера можно получить в сравнении с обычным приложением опубликованным как self-hosted? В ряде случаев я могу понять удобство контейнеров (приложения на python или чем то подобном где так и не смогли победить проблему dll hell, а точнее package hell, или предподготовленные сборки со сложной конфигурацией). Но в случае .net, где нет проблем с версионностью библиотек, пока не могу найти ни одного плюса от них. По тестам, контейнеры еще и дают 2-5% падение производительности.
Чтобы четче обозначить, я сравниваю подход когда какой то инструмент публикует .net приложение просто в каталог и устанавливает в качестве службы, и подход когда это же приложение будет развернуто в контейнере. Интересует реальный профит который можно от этого получить.
Достаточно написать FROM scratch в Dockerfile и мы получим абсолютно пустую файловую систему и сможем запустить свою программу без файлов вообще.
Есть у меня большие сомнения, что вы пробовали это сделать ))
А я попробовал - нужен как минимум линкер (без него docker жалуется, что ваш executable файл вообще не найден)
Если уже добавлен линкер, то тогда система будет уже вразумительно ругаться на отсуутсвующие dll-ки.
Вот пример моего минимального образа. Здесь minimal-hello - это c-шный hello-world собранный статически (вроде, судя по настройкам в Eclipse, но... мог здесь и налажать)
FROM scratch
ADD minimal-hello /
# linker
ADD sys_linux_root/lib64/ld-linux-x86-64.so.2 /lib64/
# Standard C lib
ADD sys_linux_root/usr/lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/
CMD ["./minimal-hello"]
А вот немножко больший образ
FROM scratch
ADD minimal-hello /
# optional
ADD sys_linux_root/bin/* /bin/
#
# Required for ANY minimal executables
# * dynamic linker
ADD sys_linux_root/lib64/ld-linux-x86-64.so.2 /lib64/
# Standard C lib
ADD sys_linux_root/usr/lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/
# Additional libs fo ls/sh/bash
ADD sys_linux_root/usr/lib/x86_64-linux-gnu/* /usr/lib/x86_64-linux-gnu/
# Some data files
COPY hello.txt /
COPY hello.txt /hello-002.txt
CMD ["./minimal-hello"]
Содержимое моего sys_linux_root.
Здесь bash, cat, sleep, ls, sh просто, чтобы поиграться и дополнительные ddl-ки, нужные им или программам, которые делают хоть что-то полезное. Все эти файлы скопированы просто с моего текущего линукса.
.
├── bin
│ ├── bash
│ ├── cat
│ ├── ls
│ ├── sh
│ └── sleep
├── lib64
│ └── ld-linux-x86-64.so.2
└── usr
└── lib
└── x86_64-linux-gnu
├── libc.so.6
├── libdl.so.2
├── libpcre2-8.so.0
├── libpthread-2.31.so
├── libpthread.so
├── libpthread.so.0
├── libselinux.so.1
└── libtinfo.so.6
Если кто-то умудрился сделать образ даже без линкера, то будет интересно послушать.
PS: Жду минусов, как обычно ))
Вы правы и про линкер и про меня! Проблема в том, что надо все библиотеки залинковать статически в бинарь. Примерно вот так:
FROM alpine AS build
WORKDIR /static
RUN apk add build-base
COPY hello.c .
RUN gcc -o hello --static hello.c
FROM scratch
COPY --from=build /static/hello /
CMD [ "/hello" ]
Тогда все заработает. В этом бинаре нет ни одного динамического символа, только syscall в ядро с которыми и работает докер.
Конечно мой пример супер прост как только вы например подключите стандартную библиотеку вам понадобится куча возможностей системы и с ними файлы конфигурации. Например /etc/passwd для определения текущего пользователя, сертификаты для web и тп.
При подготовки статьи я не проверял вся на c я проверил все только на go. Go умеет делать все статическим по этому там такое сильно проще. Чтобы не проверять пример я нашел его в интернете: https://github.com/jeremyhuiskamp/golang-docker-scratch/blob/main/Dockerfile обратите внимание на 14 строку там копируются сертификаты .
Спасибо за вопрос!
Distroless контейнеры