Статья 7 из 10, в которой речь пойдёт об LXC. Какие требования для создания непривилегированных контейнеров? Как предварительно созданные контейнеры LXC обходят ограничения? Как правильно использовать непривилегированные контейнеры?
Поддержка непривилегированных контейнеров (unprivileged containers), пожалуй, важнейшая новая возможность в LXC 1.0. В предыдущих постах было вскользь упомянуто, что не смотря на раздельные пространства имён (namespace), UID 0 (root) в контейнере по-прежнему равен UID 0 вне контейнера в хосте. Это означает, что если каким-то образом получить доступ к ресурсам хоста через proc, sys или системные вызовы (syscalls), то потенциально можно сбежать из контейнера и стать root'ом в хосте.
Для избежания такой ситуации и были разработаны пользовательские пространства имён (user namespaces). Их создание и реализация плод многолетних усилий по продвижению патчей, необходимых в ядре. И вот в ядре linux 3.12 окончательно появилось всё необходимое, что позволяет обезопасить LXC контейнеры через user namespaces.
И как же работают эти user namespaces? Говоря упрощённо, каждому пользователю, который будет использовать контейнеры в системе, присваивается диапазон неиспользуемых UID и GID, а в идеале - все 65536. Затем эти UID и GID с помощью утилит newuidmap и newgidmap отображаются (map) из этих UID и GID в виртуальные UID и GID для пользовательского пространства имён.
Можно для примера создать контейнер и указать 4 поля (1 поле - u или g, 2 поле - первый UID в контейнере, 3 поле - первый UID в хосте, 4 поле - диапазон):
lxc.id_map = u 0 100000 65536
lxc.id_map = g 0 100000 65536
Это будет означать, что есть одна карта UID и одна карта GID у моего контейнера, которая UID/GID контейнера от 0 до 65536 ставит в соответствие с UID/GID хоста от 100000 до 165536 (100000+65536). Чтобы это сопоставление работало, нужно указать пользователю (в примере stgraber учётная запись на ноутбуке Стефана Грабера) эти диапазоны:
В файле /etc/subuid - stgraber:100000:65536
В файле /etc/subgid - stgraber:100000:65536
LXC обновлён и все его инструменты умеют работать с непривилегированными контейнерами.
Стандартные пути имеют свои "непривилегированные" эквиваленты:
- /etc/lxc/lxc.conf => ~/.config/lxc/lxc.conf
- /etc/lxc/default.conf => ~/.config/lxc/default.conf
- /var/lib/lxc => ~/.local/share/lxc
- /var/lib/lxcsnaps => ~/.local/share/lxcsnaps
- /var/cache/lxc => ~/.cache/lxc
Таким образом, пользователь может создавать контейнеры, в которых он будет обладать привилегиями root'а, но это не будет давать ему никаких супер прав в системе хоста.
Правда остаются вопросы о таких системных вещах как сетевые устройства и изменение конфигурации сетевого моста. Пользователь должен иметь возможность работать с ними и изменять их, но не обладать правами root'а в хост-машине. Для реализации задуманного написана утилита lxc-user-nic, которая единственная из всех утилит LXC 1.0 обладает SETUID битом. Задача lxc-user-nic проста: она парсит конфигурационный файл и на основе его создаёт сетевые устройства для пользователя и соединяет их в сетевой мост. Для предотвращения злоупотреблений, можно ограничить количество устройств и запросы на соединения в сетевой мост.
Для примера, в файле /etc/lxc/lxc-usernet можно задать ограничение для пользователя stgraber:
stgraber veth lxcbr0 10
Эта запись разрешает создать 10 устройств типа veth и соединить их сетевым мостом с lxcbr0. Как будут созданы veth устройства - решать вам. Все 10 в одном контейнере (не имеет смысла из-за сетевого моста) или по одному veth в каждом из 10 контейнеров. Ограничение только в количестве, нет ни каких ограничений от LXC, кроме лимитов самого ядра linux.
Благодаря полной поддержке и реализации user namespaces в ядре и единственной setuid утилите, реализована поддержка непривилегированных контейнеров.
Требования к системе.
На дату 28 января 2014 года, требования для нормальной работы непривилегированных контейнеров следующие:
- Ядро linux >= 3.13.
- Включенная поддержка User namespaces в ядре.
- Последняя версия shadow с поддержкой subuid/subgid.
- Cgroups с контролем отдельных пользователей на всех контроллерах.
- LXC 1.0 beta2 или новее.
- Версия PAM (Pluggable Authentication Modules) с патчем к loginuid.
Всем этим требования точно удовлетворяет Ubuntu 14.04 Trusty Tahr, которая пока находится в разработке, обновлённая на дату 28 января 2014 или актуальнее.
Предварительно созданные контейнеры LXC.
Пользовательские пространства имён (user namespaces) для создания непривилегированных контейнеров идут с рядом ограничений. В user namespaces вы не сможете использовать mknod для создания блочных или символьных устройств, так как позволять такое, значит разрешать доступ к хосту. Так же нельзя использовать монтирование ext разделов и mount -t loop, даже если дан доступ к блочному устройству.
Эти ограничения, естественно, не конец света, но в повседневном использовании станут большой проблемой при bootstrap'е контейнера и вызове утилит debootstrap, yum и других, которые точно захотят выполнить эти запрещённые действия и получат отлуп.
Некоторые шаблоны операционных систем были подправлены, чтобы обойти часть ограничений, если пользователь захочет использовать на своём компьютере именно непривилегированные контейнеры. Но проект LXC исходит из того, что пользователь не должен обладать знаниями создателей дистрибутивов Linux и поэтому предложено более простое решение.
Написан новый шаблон download, который вместо того чтобы пытаться создать на локальной машине образ нужной системы, просто связывается с сервером, который хранит ежедневные подготовленные rootfs и конфигурации большинства шаблонов операционных систем.
Образы делает Jenkins сервер, используя несколько машин. Сам процесс сборки очень прост. С базовым chroot и стандартными шаблонами запускается сборка с нужным релизом и архитектурой. Получившийся rootfs сжимается и вместе с базовым конфигом и метаданными (время устаревания, файлы шаблона, ...) сохраняются на сервере, подписываются GPG ключом и публикуются в веб.
Со стороны клиента нужно всего лишь соединиться по https (сервер поддерживает DNSSEC и IPv6), взять подписанный индекс всех доступных образов, проверить наличие нужного дистрибутива, релиза и архитектуры. Затем можно скачать нужное, проверить криптографическую подпись и сохранить к себе в локальный кэш. Любое создание контейнера использует данный кэш, но только пока не истечёт указанный период, после чего будет скачана новая копия с сервера.
Текущий список образов, которых можно получить с ключом --list
--- DIST RELEASE ARCH VARIANT BUILD --- debian wheezy amd64 default 20140116_22:43 debian wheezy armel default 20140116_22:43 debian wheezy armhf default 20140116_22:43 debian wheezy i386 default 20140116_22:43 debian jessie amd64 default 20140116_22:43 debian jessie armel default 20140116_22:43 debian jessie armhf default 20140116_22:43 debian jessie i386 default 20140116_22:43 debian sid amd64 default 20140116_22:43 debian sid armel default 20140116_22:43 debian sid armhf default 20140116_22:43 debian sid i386 default 20140116_22:43 oracle 6.5 amd64 default 20140117_11:41 oracle 6.5 i386 default 20140117_11:41 plamo 5.x amd64 default 20140116_21:37 plamo 5.x i386 default 20140116_21:37 ubuntu lucid amd64 default 20140117_03:50 ubuntu lucid i386 default 20140117_03:50 ubuntu precise amd64 default 20140117_03:50 ubuntu precise armel default 20140117_03:50 ubuntu precise armhf default 20140117_03:50 ubuntu precise i386 default 20140117_03:50 ubuntu quantal amd64 default 20140117_03:50 ubuntu quantal armel default 20140117_03:50 ubuntu quantal armhf default 20140117_03:50 ubuntu quantal i386 default 20140117_03:50 ubuntu raring amd64 default 20140117_03:50 ubuntu raring armhf default 20140117_03:50 ubuntu raring i386 default 20140117_03:50 ubuntu saucy amd64 default 20140117_03:50 ubuntu saucy armhf default 20140117_03:50 ubuntu saucy i386 default 20140117_03:50 ubuntu trusty amd64 default 20140117_03:50 ubuntu trusty armhf default 20140117_03:50 ubuntu trusty i386 default 20140117_03:50
Шаблоны написаны и тщательно протестированы для работы на любой системе, где есть POSIX совместимый shell и wget. GPG рекомендуется, но можно отключить на свой страх и риск. Некоторые шаблоны могут повторно использоваться на вашем сервере для создания контейнеров из центрального ресурса, пользуясь механизмом истечения времени, чтобы автоматически предлагать обновлённые шаблоны.
В то время как данные шаблоны призваны помочь в создании непривилегированных контейнеров с их ограничениями, они прекрасно работают с системными контейнерами. Так что даже в системах, которые не поддерживают непривилегированные контейнеры, смело можно делать:
lxc-create -t download -n p1 -- -d ubuntu -r trusty -a amd64
И данная команда даст вам последнюю 64 битную сборку Ubuntu 14.04 Trusty Tahr.
Использование непривилегированных контейнеров.
Описание ограничений закончилось и можно наконец-то приступить к использованию непривилегированных контейнеров. Напоминаю, что сейчас всем условиям удовлетворяет лишь актуальная, ещё разрабатываемая, Ubuntu 14.04. Поэтому сначала обновитесь и доставьте нужное, если не установлено.
sudo apt-get update sudo apt-get dist-upgrade sudo apt-get install lxc systemd-services uidmap
Временный обход, пока мы ждём нового менеджера cgroup в Ubuntu, заставляет нас добавить в файл /etc/init/lxc-unpriv-cgroup.conf строки
start on starting systemd-logind and started cgroup-lite script set +e echo 1 > /sys/fs/cgroup/memory/memory.use_hierarchy for entry in /sys/fs/cgroup/*/cgroup.clone_children; do echo 1 > $entry done exit 0 end script
Этот трюк необходим, так как logind не конфигурирует нужные для LXC use_hierarchy и clone_children. После этого необходим рестарт вашего компьютера для правильной конфигурации cgroup.
Нужно присвоить себе набор UID и GID.
sudo usermod --add-sub-uids 100000-165536 $USER sudo usermod --add-sub-gids 100000-165536 $USER
Создаём ~/.config/lxc/default.conf с содержимым:
lxc.network.type = veth lxc.network.link = lxcbr0 lxc.network.flags = up lxc.network.hwaddr = 00:16:3e:xx:xx:xx lxc.id_map = u 0 100000 65536 lxc.id_map = g 0 100000 65536
И в файле /etc/lxc/lxc-usernet вы как администратор системы должны разрешить пользователю (наверное себе) использовать сетевые интерфейсы и возможность сетевого моста с физической картой хоста:
ваш-аккаунт veth lxcbr0 10
Всё что нужно - сделано! Теперь создаём первый непривилегированный контейнер:
lxc-create -t download -n p1 -- -d ubuntu -r trusty -a amd64
Должно выводиться что-то типа
Setting up the GPG keyring Downloading the image index Downloading the rootfs Downloading the metadata The image cache is now ready Unpacking the rootfs --- You just created an Ubuntu container (release=trusty, arch=amd64). The default username/password is: ubuntu / ubuntu To gain root privileges, please use sudo.
После успешного создания можно запускать свой контейнер от обычной учётной записи.
$ lxc-start -n p1 -d
$ lxc-ls --fancy
NAME STATE IPV4 IPV6 AUTOSTART ------------------------------------------ p1 RUNNING UNKNOWN UNKNOWN NO
Работает! Контейнер p1 - RUNNING! Теперь можно получить консоль запущенного непривилегированного контейнера p1 с помощью lxc-console или через SSH, узнав IP адрес через ARP таблицу arp -n
. Как вы могли уже заметить, IP адрес в статусе UNKNOWN, потому что в настоящее время LXC не может присоединить (attach) непривилегированные контейнеры к пространству имён. Это так же означает, что некоторые поля lxc-info будут пустыми и нельзя использовать lxc-attach. Сейчас разработчики ищут пути, чтобы реализовать нужное в ближайшем будущем.
Так же есть несколько проблем с управлением заданиями в ядре и с PAM, что приводит иногда к вызову lxc-start с довольно странной консолью, где sudo может не работать.
SSH может не работать на некоторых дистрибутивах. Патч уже послан в апстрим, но он, к сожалению, не охватывает все проблемные случаи и, самое главное, его ещё нет во всех релизах.
Довольно много улучшений с непривилегированными контейнерами не попадут в релиз LXC 1.0 в феврале 2014 года. Естественно, пока рано ожидать многого от непривилегированных контейнеров, но сделан огромный шаг по сравнению что было раньше. Сделан хороший задел на будущее, в котором можно будет использовать непривилегированные контейнеры в ряде интересных случаев.
Предыдущая статья LXC 1.0: Безопасность.
Следующая статья LXC 1.0: Скрипты и API.
Дополнительные материалы:
Серия статей LXC 1.0. от Стефана Грабера.
Немає коментарів:
Дописати коментар