СОБСТВЕННЫЙ DOCKER

СОЗДАЕМ ПЕСОЧНИЦУ ДЛЯ ПРИЛОЖЕНИЙ СВОИМИ РУКАМИ

СОБСТВЕННЫЙ DOCKER - 839525101904
ВМЕСТО ВВЕДЕНИЯ
Для начала разберемся, что же такое эта самая пресловутая песочница, ну или контейнер, или изолированное окружение исполнения, кому как больше нравится. Фактически все эти термины относятся к одному и тому же типу технологий, а именно программным системам, позволяющим запускать приложения (или даже целые ОС) в изолированном от основной системы окружении, которое не позволит приложению ни увидеть основную систему, ни тем более выбраться в нее.
Один из ранних примеров песочницы — это системный вызов 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, передав ей несколько флагов. Попробуй для примера выполнить ледующую команду:
$ sudo unshare -p --fork --mount-proc /usr/bin/ps -aux
Несмотря на то что мы прямо попросили ps показать все системные процессы (флаги -aux), в списке процессов оказывается только сам ps. Такое происходит потому, что мы запустили ps в собственном пространстве имен процессов (флаг -p и флаги --fork и --mount-proc, чтобы смонтировать /proc по новой уже после применения пространства имен). Можешь заменить /usr/bin/ps на /usr/bin/top, чтобы убедиться, что top тоже не может «заглянуть» за пределы пространства имен.
СОБСТВЕННЫЙ DOCKER - 839525144400
Команда unshare позволяет запускать приложение и в любых других пространствах имен, поэтому, объединив ее с командой chroot, мы можем создать по-настоящему изолированную песочницу:
$ sudo unshare -piumU --fork --mount-proc chroot ~/sandbox
/bin/ls -l /
Эта команда создаст все перечисленные выше пространства имен (кроме сетевого) и запустит /bin/ls внутри них. Это уже почти настоящее виртуальное окружение с собственным точками монтирования, юзерами, списком процессов. Единственное, чего не хватает, — это выделенный сетевой стек (сетевое пространство имен). Его мы создадим с помощью другой команды — ip.
Но для начала нам нужен виртуальный сетевой мост, который будет служить выходом виртуального окружения в сеть.
Создаем сетевой мост и подключаем его к физическому сетевому интерфейсу enp3s0 (у тебя его имя, скорее всего, будет другим, запусти ifconfig, чтобы узнать точно):
$ sudo brctl addbr br0
$ sudo brctl addif br0 enp3s0
$ sudo dhcpcd br0
Далее создаем новое сетевое пространство имен (назовем его sandbox), а также пару сцепленных друг с другом виртуальных сетевых интерфейсов (veth), один из которых (eth1) пробросим в это пространство имен:
$ sudo ip netns add sandbox
$ sudo link add veth1 type veth peer name eth1
$ sudo link set eth1 netns sandbox
Подключаем виртуальный сетевой интерфейс к мосту и запускаем dhcpcd для получения IP-адреса:
$ sudo brctl addif br0 veth1
$ sudo ip link set veth1 up
$ sudo ip netns exec dhcpcd eth1
В качестве последнего штриха добавляем в виртуальное окружение адрес DNS-сервера (это будет гугловский 8.8.8.8):
$ sudo mkdir ~/sandbox/etc
$ sudo echo 'nameserver 8.8.8.8' > ~/sandbox/etc/resolv.conf
Осталось только запустить наше виртуальное окружение в полученном пространстве имен:
$ sudo ip netns exec unshare -piumU --fork --mount-proc chroot
~/sandbox /bin/ls -l /

ВОЗДВИГАЕМ СТЕНЫ
Для примера с /bin/ls это неважно, но настоящее виртуальное окружение также должно ограничивать доступные приложению ресурсы, будь то процессор, память или пропускная способность дисковой подсистемы. Для этих целей в ядре Linux есть механизм cgroups (control groups), позволяющий объединить процессы в группы и привязать к ним так называемые контроллеры, которые будут ограничивать те или иные ресурсы. Всего существует около десятка различных контроллеров, наиболее полезные из них:
• blkio — устанавливает лимиты на скорость ввода-вывода;
• cpu — позволяет ограничить использование процессора;
• cpuset — привязывает группу процессов к определенному процессорному
ядру;
• devices — открывает/закрывает доступ к определенным устройствам;
• freezer — позволяет замораживать процессы в группе;
• memory — устанавливает лимиты на использование оперативной памяти.
Мы для простоты будем использовать только два контроллера: cpu и memory.
Для начала создадим группу контроллеров:
$ sudo cgcreate -g cpu,memory:/sandbox
Далее установим лимиты. С памятью все просто, указываем прямым текстом 128 Мбайт:
$ sudo cgset -r memory.limit_in_bytes=128M sandbox
С процессором дела обстоят несколько сложнее. Здесь необходимо указывать период процессорного времени в миллисекундах и квоту, выделенную группе процессов на этот период. Например, если мы хотим, чтобы наша группа процессов (приложение) получала доступ к процессорным ресурсам на одну десятую секунды каждую секунду, то нужно выполнить следующую команду:
$ sudo cgset -r cpu.cfs_period_us=1000000
-r cpu.cfs_quota_us=100000 sandbox
Обрати внимание, что данная настройка относится только к одному процессору, то есть на двухъядерной системе приложение будет получать процессор в два раза реже, а на четырехъядерной — в четыре раза реже. Еще один момент: данная настройка вовсе не значит, что приложение получит 10% процессорной мощности, она всего лишь гарантирует, что приложение не будет получать доступ к процессору реже, чем мы указали. Если система простаивает, то планировщик может выделить приложению гораздо больше времени для работы.
Ну и в завершение нам необходимо запустить наше приложение под управлением cgroups:
$ sudo cgexec -g cpu,memory:/sandbox ip netns exec unshare -piumU --fork --mount-proc chroot ~/sandbox /bin/ls -l /
Вуаля, мы получили полноценное виртуальное окружение, полностью отрезанное от основной системы, с ограничением ресурсов и выходом в сеть через виртуальный шлюз.

СОБИРАЕМ ВСЕ ВМЕСТЕ
Теперь, когда мы знаем, как создать песочницу, мы можем написать скрипт, который делает это все за нас. Вот он:

https://yadi.sk/d/X5opmZccvsPkt

Пользоваться скриптом очень просто. Достаточно просто указать имя приложения:
$ sudo
sandbox.sh ps
Далее скрипт создаст выделенную песочницу в каталоге ~/sandbox, скопирует в него приложение и его зависимости, настроит сетевой мост и cgroups и запустит приложение, после чего удалит песочницу и сбросит настройки.
Для простоты аргументы игнорируются. Также не пытайся с его помощью запустить сложный, а тем более графический софт, зависящий от файлов настроек и других данных, которые этот скрипт не умеет копировать в песочницу.

ВЫВОДЫ
Ядро Linux имеет все необходимые средства для создания изолированных окружений исполнения. Те же самые технологии можно использовать для запуска не только отдельно взятых приложений, но и целых дистрибутивов. Для этого достаточно скопировать в каталог песочницы все файлы дистрибутива, смонтировать каталоги /dev и /proc, а затем запустить /bin/init.
Ну а популярность Docker обусловлена всего лишь тем, что он позволил делать все это с помощью одной простой команды, без необходимости самому копировать зависимые библиотеки и возиться с настройками.

#delvin #xakep #system #sandbox

Комментарии

  • 29 сен 2016 09:13
    Дим, то-то и оно. Безо всего - штатными средствами! ;-)
  • 29 сен 2016 09:17
    Да ну?

    $ sudo  systemd-nspawn

    sudo: systemd-nspawn: command not found

    $ sandbox --help

    Usage: sandbox [options] [program [program args...]]


    Sandbox will start up a sandbox session and execute the specified program.

    If no program is specified, an interactive shell is automatically launched.

    You can use this to quickly test out sandbox behavior.


    Совсем не Security. ;-)

  • 29 сен 2016 09:31
    ЗАЧЕМ? :-O
    Докер же(еще не ставил)!
  • 29 сен 2016 09:34
    КОСТРАТ:

    $ ls /etc/systemd/

    network  system

    $ ls /etc/systemd/network/

    $ ls /etc/systemd/system/

    dhcrelay4.service.d  distccd.service.d  ntpdate.service.d  sntp.service.d dhcrelay6.service.d  hddtemp.service.d  slapd.service.d

  • 29 сен 2016 09:38
    LVM2 и initramfs не использую от слова вообще!
    А это, чтобы совсем понятно было:

    $ sudo eselect profile list

    Available profile symlink targets:

     [1]   default/linux/amd64/13.0 *

     [2]   default/linux/amd64/13.0/selinux

     [3]   default/linux/amd6...........

  • 29 сен 2016 09:38
    "АГААаа!"© Двое из ларца.
  • 29 сен 2016 09:43
    "Туда-сюда" ФС дергаю редко, так что LVM "в хрен не падал", но если приспичит и инфу терять нельзя, то юзаю cfdisk+fsextender(прога c github, я тему про нее выкладывал).
  • 29 сен 2016 09:45
    Да, точно, не systemd - загружаюсь через OpenRC.
  • 29 сен 2016 09:50
    А смысл всю систему корежить? Как сказал один Великий человек:
    "Работает - не трогай!" ©
    И ведь работает "как часы". Изредка "падает" лиса, но настолько редко, что даже БЕСИТ(бесит, что редко) - остальное еще надо постараться "положить".
    НЕ MUSTDIE ОДНОЗНАЧНО!

    В бытность мою под ХР(я рассказывал), система крашилась ОЧЕНЬ редко, но проги "вылетали" с завидной периодичностью. Прога в день - норма. Особенно, если AVRStudio или OllyDBG запущено - что нибудь да накроется.
  • 29 сен 2016 09:55
    Я тоже - имел ввиду TORBrowser.
  • 30 сен 2016 08:22

    ] ~ % ls /etc/systemd

    ls: невозможно получить доступ к /etc/systemd: Нет такого файла или каталога     % ls /usr/lib/systemd/system

    mdadm-grow-continue@.service  mdadm-last-resort@.timer  mdmonitor.service

    mdadm-last-resort@.service    mdmon@.service

    несовместимый с ядром мусор попадает теперь автоматом -о какой изоляции говорится непонятно у ядра нет четкого контроля над библиотеками(мира) что подсунешь то и будет работать системы инициализации штатны для дистров к системе отношения не имеют.Пусть пасутся в контейнерах -ума набираются те кому надо это. % sudo epro show


    === Enabled Profiles: ===


           arch: x86-64bit

          build: stable

        subarch: amd64-k8

         flavor: core

        mix-ins: no-systemd

        mix-ins: X

        mix-ins: media-pro