Как-то промелькнула новость "Приложения для GNOME теперь могут работать в полностью изолированном контейнере" про первую программу-игру Neverball, которую разработчик GNOME Александр Ларсон (Alexander Larsson) запихал в контейнер. И вокруг этой новости с другими пользователями возник диспут, а вот возьмёт программа в контейнере и получит доступ к хосту. Технология контейнеров начинает набирать обороты популярности, но всё равно остаётся малоизвестной широкой публике. Я нашёл довод в пользу программа-в-контейнере, приведя ситуацию со Skype, который в 64битной системе тянет 32битные копии библиотек, и вот, его подлеца, было бы круто запихать в контейнер. Но настоящие спецы по контейнерам, такие как Андреа Корбеллини (Andrea Corbellini), легко и просто объяснят сложные моменты. Читаем - безопасность и контейнеры!
Со времён своего первого релиза в 2008 году, LXC (англ. Linux Containers) широко распространился на серверах. Сегодня LXC становится популярным в смысле стратегии развёртывания ПО, благодаря инструментам Docker и LXD (Linux Container Daemon). LXC и Docker используются не только для создания модульной архитектуры сложных программных комплексов, но и как отличный механизм запуска стороннего, непроверенного кода (untrusted code) в изолированной среде.
Можно согласиться, что LXC и Docker отличные системы и работают хорошо, но могут ли они дать безопасность?
Здесь в статье не будет полного погружения в детали LXC. Информацию про пространства имён (namespaces) и cgroups вы можете получить в Интернете или в статье LXC 1.0: Безопасность. Хотелось бы показать что LXC может делать, чего не может делать и что ему позволено делать в конфигурации по умолчанию. Будет представлен список для тех, кто хотел бы использовать безопасно LXC, но не знал на что стоит обратить внимание.
LXC ограничивает процессы, благодаря двум возможностям ядра linux: пространства имён (namespaces) и cgroups. Они дают механизм контроля и ограничений для ресурсов типа памяти и файловой системы. Для примера, можно ограничить полосу пропускания, используемую процессами в контейнере, можно ограничить приоритет планировщика CPU и так далее.
Процессы внутри контейнера-гостя не могут:
С другой стороны, процессы внутри гостевого контейнера LXC получают для себя среду, идеальную для запуска операционной системы. Можно запускать Init, читать /proc, получать доступ в Интернет. Это почти всё что делает LXC и это даётся по умолчанию. Docker, как обёртка над LXC, включает в себя утилиты, позволяющие легко оперировать и управлять контейнерами. Но всё что относится LXC, относится и к Docker так же.
Звучит здорово, но нужно учитывать, что есть вещи, которые нужно знать ...
Сам LXC несколько неполон. Имеется ввиду, что такие специальные файловые системы как procfs и sysfs не подменить. Вы можете легко у хостовой операционной системы изменить /proc/sys/kernel/panic или /sys/class/thermal/cooling_device0/cur_state. Причина почему LXC "не полон" нам не важна. Важно, что неприятные действия блокируются уже не самим LXC, а профилями безопасности систем мандатного доступа типа AppArmor или SELinux. Таким образом, контексты безопасности AppArmor или SELinux требуются для безопасного использования LXC. Без них пользователь root в гостевом контейнере может взять под контроль хост. Важно убедиться, что в вашей системе AppArmor или SELinux настроены и работают корректно. Если вы используете такой проект как Grsecurity, то не забудьте его настроить в ручную.
LXC предлагает решения для ограничения потребления ресурсов, но по умолчанию сам не налагает какие-либо лимиты. Вы сами должны сконфигурировать ограничения для гостевой системы. С настройками по умолчанию, вы можете в гостевой среде устроить fork-бомбы, запросить огромное выделение ОЗУ, занять всё процессорное время, забить весь файловый ввод-вывод. Для этого не нужно особых привилегий и об этом стоит помнить при запуске непроверенного кода.
Для ограничения потребления ресурсов в LXC нужно открыть конфигурационный файл данного контейнера и выставить нужное в стиле lxc.cgroup.<нужная-подсистема>. Для примера, ограничение гостю в потребление ОЗУ размером 512 Мб будет выглядеть так: lxc.cgroup.memory.limit_in_bytes = 512M
Заметьте, что контейнер, исчерпав ОЗУ, начнёт использовать swap без ограничений. Если это не то что вы хотели, то нужно так же указать lxc.cgroup.memory.memsw.max_usage_in_bytes = 512M. Эти две опции возможно потребуют от вашего ядра cgroup_enable=memory и swapaccount=1. У Докера есть параметр --lxc-conf, который и поможет выставить опции LXC.
Что LXC не умеет, так это ограничивать использование хранилища. К счастью, LXC хорошо интегрируется с LVM (brtfs, zfs и overlayfs), что позволяет через них легко лимитировать дисковое пространство. Для примера, вы можете создать логический том по одному на каждого гостя. Объём отдельных томов не даст гостю расти бесконечно.
Процессы внутри контейнера LXC, по умолчанию, могут читать /dev/random и потреблять энтропию хоста. Это может привести к проблемам, когда требуется много бит случайности (при генерации ключей, к примеру). Если такое поведение вам не нужно, то можете сконфигурировать контейнер, лишив доступа к символьному устройству 1:8 (random) и 1:9 (urandom). Отказать к /dev/random недостаточно, так как внутри гостя разрешён mknod, с помощью которого можно вернуть недостающее. Только заметьте, что ограничение в random может поломать приложения внутри гостя, которым может понадобиться случайность. Выходом может стать Энтропия-как-сервис.
Контейнер может запускать обычный пользователь. Это означает, что root (UID=0) внутри гостя не будет совпадать с root (UID=0) хоста и многие потенциальные дыры в безопасности нельзя эксплуатировать. К сожалению, Docker пока не поддерживает непривилегированные контейнеры. Но Docker не является обязательным требованием и является надстройкой инструментом сверху, поэтому можете смело работать с LXC напрямую.
Программы, подобные Apache, будут жаловаться что не могут сделать ulimit, так как это привилегия реального root. Если вам нужно запускать программы, требующие специальных привилегий, то нужно будет настроить так, чтобы не было жалоб или решиться на использование Capabilities, но не злоупотребляйте ими, иначе огребёте больше проблем, чем решите их.
LXC, Docker и вся экосистема вокруг них - зрелая и стабильная. Они готовы в продакшн и если всё настроено вами правильно, то гости не вызовут проблем у хоста. Тем не менее считать ли контейнеры безопасными или нет, зависит от вас: Для чего вы используете контейнеры? Кому вы даёте доступ? Какие привилегии вы даёте, и какие действия ограничиваете?
Всегда помните, что LXC даёт по умолчанию, а что нет. Особенно, если вы не доверяете коду программы. Тщательно проверяйте конфигурационный файл контейнера прежде чем открыть дверь другим.
Цикл статей про все аспекты LXC от Стефана Грабера (Stéphane Graber).
Дата последней правки: 2023-12-28 12:55:42