Авторские статьи

Безопасны ли контейнеры LXC и Docker?


Как-то промелькнула новость "Приложения для 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 может делать.

LXC ограничивает процессы, благодаря двум возможностям ядра linux: пространства имён (namespaces) и cgroups. Они дают механизм контроля и ограничений для ресурсов типа памяти и файловой системы. Для примера, можно ограничить полосу пропускания, используемую процессами в контейнере, можно ограничить приоритет планировщика CPU и так далее.

Процессы внутри контейнера-гостя не могут:

  • напрямую взаимодействовать с процессами хоста или другого контейнера LXC.
  • получать доступ к корневой файловой системе, если не указано специально.
  • получать доступ к специальным устройствам (блочным устройствам, сетевым интерфейсам, ...), если не указано специально.
  • монтировать любые файловые системы.
  • выполнять определённые ioctls, системные вызовы (syscalls) и прерывания, влияющие на поведение хостовой системы.

С другой стороны, процессы внутри гостевого контейнера 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), что позволяет через них легко лимитировать дисковое пространство. Для примера, вы можете создать логический том по одному на каждого гостя. Объём отдельных томов не даст гостю расти бесконечно.

Заметки о /dev/random

Процессы внутри контейнера 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

RSS vasilisc.com   


Разделы

Главная
Новости
Ворох бумаг
Видео Linux
Игры в Linux
Безопасность
Статьи об Astra Linux
Статьи о FreeBSD
Статьи об Ubuntu
Статьи о Snappy
Статьи об Ubuntu Phone
Статьи о Kubuntu
Статьи о Xubuntu
Статьи о Lubuntu
Статьи об Open Source
Карта сайта