вівторок, 28 січня 2014 р.

LXC 1.0: Непривилегированные контейнеры

Статья 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. от Стефана Грабера.

Немає коментарів:

Дописати коментар

HyperComments for Blogger

comments powered by HyperComments