В прошлой статье мы рассмотрели простые решения изоляции, которые сможет использовать даже самый ленивый и неискушенный пользователь. Но как работают эти системы? Какие технологии лежат в их основе и почему Docker, не открыв ничего нового, стал настолько популярен? Попробуем ответить на эти вопросы, а заодно на коленке соберем свою собственную песочницу на базе ядра Linux.

 

Вместо введения

Для начала разберемся, что же такое эта самая пресловутая песочница, ну или контейнер, или изолированное окружение исполнения, кому как больше нравится. Фактически все эти термины относятся к одному и тому же типу технологий, а именно программным системам, позволяющим запускать приложения (или даже целые ОС) в изолированном от основной системы окружении, которое не позволит приложению ни увидеть основную систему, ни тем более выбраться в нее.

Один из ранних примеров песочницы — это системный вызов chroot и одноименная команда. Он появился еще в Version 7 оригинального UNIX в 1979 году, а в 1982-м перекочевал в BSD. Chroot выполняет очень простую вещь: он позволяет сделать так, чтобы корневым каталогом для приложения стал не настоящий корень, а указанный пользователем (или программистом) каталог. В результате приложение оказывается как бы замкнуто внутри этого каталога, не зная, что существует что-то за его пределами.

На примере размещение приложения в chroot выглядит приблизительно так. Для начала создаем «корневой» каталог для приложения и копируем его туда:

$ sudo mkdir ~/sandbox
$ sudo mkdir ~/sandbox/bin
$ sudo cp `which ls` ~/sandbox/bin

Далее выясняем, от каких библиотек зависит приложение, и тоже копируем их в chroot. Это необходимо сделать, потому что после запуска в chroot оно просто не сможет увидеть и, как следствие, загрузить библиотеки основной системы.

$ ldd `which ls`
linux-vdso.so.1 (0x00007ffe51efd000)
libcap.so.2 => /usr/lib/libcap.so.2 (0x00007f19da03c000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f19d9c9b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f19da240000)
$ sudo mkdir -p ~/sandbox/usr/lib
$ sudo mkdir ~/sandbox/lib64
$ sudo cp /usr/lib/libcap.so.2 /usr/lib/libc.so.6 ~/sandbox/usr/lib
$ sudo cp /lib64/ld-linux-x86-64.so.2 ~/sandbox/lib64

Библиотека linux-vdso.so.1 — виртуальная и входит в состав libc, поэтому ее мы пропускаем. Теперь, когда все готово, можно запустить наш bin/ls в chroot:

$ sudo chroot ~/sandbox /bin/ls -l /
total 12
drwxr-xr-x 2 0 0 4096 Jul 12 07:20 bin
drwxr-xr-x 2 0 0 4096 Jul 12 07:35 lib64
drwxr-xr-x 3 0 0 4096 Jul 12 07:34 usr

Как видишь, хотя мы попросили команду ls отобразить содержимое корневого каталога, нам показали содержимое ~/sandbox. Бинарник ls замкнут в этом каталоге, и файлов основной системы для него как бы не существует. Казалось бы, вот она, песочница для запуска сервисов и небезопасного софта! Да, долгое время chroot использовали для этих целей, но есть одна тонкость. Для ls виртуальной стала только файловая система (а если точнее — файловая иерархия), тогда как все остальные сущности ядра (список процессов, сетевой стек, разделяемая память) остались общими с основной системой.

Если ты создашь внутри chroot каталог proc, смонтируешь к нему файловую систему procfs, а затем закинешь в chroot и запустишь бинарник ps, то обнаружишь, что из chroot видно все процессы основной системы. IP-адрес и правила брандмауэра также останутся неизменными. Приложения, использующие разделяемую память и другие механизмы IPC, смогут общаться с процессами хост-системы. А процесс, работающий в chroot с правами root, сможет легко покинуть песочницу.

 

Укрепляем песочницу

Для решения этих проблем во FreeBSD еще в 2000-м году появился механизм jail, прозванный «chroot на стероидах». Он позволил отделить процессы основной системы от процессов внутри chroot/jail, ограничить доступ к сети и переменным ядра, назначить каждому виртуальному окружению собственный hostname, а начиная с FreeBSD 9.0 — ограничить использование ресурсов.

Разработчики Linux пошли несколько другим путем и реализовали концепцию «пространств имен» (namespaces). Это уже более комплексный и универсальный механизм, позволяющий выполнить над ресурсами/сущностями ядра (список процессов, разделяемая память, сетевой стек) примерно ту же операцию, что chroot выполняет над файловой иерархией, то есть отрезать выбранное приложение от основной системы. Существует множество разных типов пространств имен:

  • PID — пространство имен процессов, ограничивает область видимости только процессами, которые были порождены самим приложением, и закрывает доступ ко всем остальным процессам системы;
  • Mount — позволяет монтировать файловые системы так, чтобы они были видны только самому приложению и его потомкам;
  • IPC — отрезает процессы, использующие разделяемую память, семафоры и очереди сообщений, от процессов хост-системы;
  • Network — создает отдельный сетевой стек для приложения и его потомков, с собственными IP-адресами, правилами брандмауэра, таблицами маршрутизации, правилами QoS и так далее;
  • UTS — позволяет привязать к приложению и потомкам собственное доменное имя;
  • User — создает собственную таблицу пользователей и групп.

Красота пространств имен в том, что их очень просто применить. Достаточно запустить приложение под управлением команды unshare, передав ей несколько флагов. Попробуй для примера выполнить следующую команду:

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


Check Also

Разгоняем микроконтроллер. Как я выжал 620 МГц из совместимой с Arduino платы

Производительности всегда не хватает. Иногда проблему можно решить, просто обновившись до …

5 комментариев

  1. Аватар

    slinkin

    09.08.2016 at 14:04

    Евгений, отличная статья. Хорошее объяснение популярной технологии, редкие но точкие ремарки с пояснением флагов, последовательный слог + полезный экспириенс по работе/конфигу Linux.

  2. Аватар

    afiskon

    09.08.2016 at 14:55

    Отличная, просто замечательная статья. Спасибо большое!

  3. Аватар

    hrapovd

    09.08.2016 at 15:33

    Не работают приведенные примеры. Вот что выдает стандартная Ubuntu Server 14.0.4 (проверил на 4х серверах):
    unshare -h

    Usage:
    unshare [options] [args…]

    Options:
    -h, —help usage information (this)
    -m, —mount unshare mounts namespace
    -u, —uts unshare UTS namespace (hostname etc)
    -i, —ipc unshare System V IPC namespace
    -n, —net unshare network namespace

    For more information see unshare(1).
    приведенных ключей -p и т.д. нет и в man

  4. Аватар

    soko1

    10.08.2016 at 10:35

    >sudo unshare -p —fork —mount-proc /usr/bin/ps -aux

    По всей видимости тут опечатка. ps сколько его помню всегда лежал в /bin

  5. Аватар

    saga111a

    14.08.2016 at 21:42

    ОТлично! неделю назад искал подобное решение, а тут прямо все как надо!

Оставить мнение