FreeBSD virtual environment management and repository

2020-10 upd: we reached the first fundraising goal and rented a server in Hetzner for development! Thank you for donating !

Внимание! Данные страницы описывают CBSD версии 13.0.x. Если вы используете более раннюю версию, рекомендуется сначала обновиться.

FreeBSD: Xorg in jail

Введение

Традиционно принято считать, что FreeBSD — неплохой выбор в качестве серверной ОС, но на роль в качестве современной рабочей станции годится слабо. Апологеты и просто горячие головы с обеих сторон этого мнения приводят разные доводы за и против и данная статья не посвящена этому бестолковому спору. Вместо этого, здесь будет произведена попытка связать и пофантазировать вокруг, казалось бы, совершенно разных технологий, таких как Xorg и Jail, а также продемонстрирован один из сценариев работы с FreeBSD в качестве рабочей станции.

Не так уж и редко на просторах интернета можно встретить информацию о том, какие дополнительные свойства от *nix системы приобретает пользователь, запускающий Xorg-based приложение в chroot(8).

На это может быть множество причин, одна из них — вы любите эксперементировать с разным количеством окружений и при этом не очень хочется превращать файловую систему в помойку (и возможно, получать конфликты между тем или иным приложением по файловой системе.)

Также, не последнюю роль играет безопасность. Никто не даст гарантии, что любой установленный софт, будь то skype, mc, screen, firefox и тд, при первом же обновлении, из-за досадной опечатки в коде не просканируют случайным образом ваш домашний каталог на предмет id_rsa ключей и known_hosts, .mysql_history, .bash_history и все ваши настройки эккаунтов из ~/.local, ~/.config , ~/.kde4 и не отправит их на сайт разработчика в качестве необходимой для дальнейшего развития обновленного софта статистики. Таким образом, chroot позволяет в этом случае не держать ваши яйца в одной корзине, что само по себе — прекрасно.

Если chroot с этим справляется, может возникнуть вопрос — «а что может дать jail при запуске в нем Xorg». Скорее всего, если знаете о различиях между jail и chroot и поскольку вы уже читаете эту статью, ответ уже найден и можно перейти к следующей главе. Если же нет, то:

Основное различие в этом плане jail от chroot — это группирование процессов по ID (jid), который может учавствовать в определенных действиях как: вести аудит только за этими процессами, возможность собирать статистику через RACCT, установка лимитов через rctl(8) (полезно для профилирования приложения). И, конечно же, привязка сетевых адресов, возможность изолированного стека и отдельной таблицы маршрутизации на все jailed процессы.

Остальные дополнительные плюсы могут уже предоставить конкретные jail management утилиты. Таким образом, то что можно получить от X-jail, зависит только от ваших знаний и фантазии, а FreeBSD как обычно, выступает лишь инструментом для решения поставленной задачи.

Приведу собственные частные случаи, что получает и использует автор.

В случае с CBSD/jail/FreeBSD, появляется возможность:

  • скачивать из репозитория преднастроенные окружения с различными вариантами популярных Windows Manager: KDE4, Fluxbox, Gnome3, LXDE и тд;
  • удобная возможность экспорта и импорта в образ;
  • репликация через zrep для инкрементальных бекапов;
  • возможность применять файловые снапшоты;
  • можно использовать cpuset(1) для привязки всех X приложений на конкретное ядро или группу ядер;
  • можно использовать различные лимиты на всю клетку, тогда как при chroot, вы можете применять лимиты через limit(1) лишь на пользователя или PID, что не так удобно.
  • В случае chroot и при наличие нескольких IP alias-ов в системе, в качестве source ip при установке соединения, приложение будет всегда использовать первый IP. В случае с jail и при наличие нескольких IP, вы можете ограничить IP для приложений лишь теми, которые вам нужны. При этом вы можете использовать PF/IPFW для подсчета трафика X-приложений, эмулировать «плохую» сеть, добавив задержки или потери на сеть этого jail, играясь с traffic bandwith и тд для изучения работы того или иного приложения в разных условиях.
  • Если у вас есть несколько рабочих станций, с помощью jail вы можете устроить «плавающее» окружение более простым образом: за какой станцией вы бы не находились, вы всегда находитесь в одном окружении, вся history, вся принятая почта, даже если она была принята при работе на другой станции — всегда с вами. Вам не нужно обновлять софт сразу в 3 системах, если вы используете 3 рабочих системы и набор ПО не меняется.

Что необходимо для X-jail

Для того, чтобы Xorg успешно работал в jail, необходимо снять ограничения jailed процессов на доступ к /dev/io и /dev/kmem устройствам. Уже долгое время существуют патчи от Alexander Leidinger aka netchild, которые предоставляют опциональность этих настроек. И сейчас, насколько мне известно, Jammie Gritton дал толчек к обсуждению о возможном включении патчей в том или ином виде в базу. Вы можете применить патчи самостоятельно и пересобрать ядро, либо можете воспользоваться командой:

			% cbsd repo action=get sources=kernel
		

И получить ядро с примененными патчами из репозитория CBSD.

Внимание: Ввиду ограничений в ресурсах, данный патч применен только для ядра FreeBSD 11 aka HEAD (на момент написания статьи) в репозитории CBSD

Внимание: allow_kmem нарушает концепцию безопасности и должна использоваться только для XJails. Не включайте эту опцию в клетках, куда имеет доступ потенциальный взломщик

При наличие этих патчей, в конфигурации CBSD jail появится опция allow_kmem, которую можно установить через cbsd jset или cbsd jconfig

Кроме того, вам может понадобиться дописать отдельное правило для devfs.ruleset(5), чтобы соответствующие устройства стали видимы в /dev каталоге jail. Например такого содержания:

			[devfsrules_unhide_xorg=8]
			add include $devfsrules_hide_all
			add include $devfsrules_unhide_basic
			add include $devfsrules_unhide_login
			add path agpgart unhide
			add path console unhide
			add path consolectl unhide
			add path dri unhide
			add path 'dri/*' unhide
			add path io unhide
			add path 'nvidia*' unhide
			add path sysmouse unhide
			add path mem unhide
			add path pci unhide
			add path tty unhide
			add path ttyv0 unhide
			add path ttyv1 unhide
			add path ttyv8 unhide
		

и указать соответствующий номер 8 в параметре devfs_ruleset из настройки jail.

На этом настройка закончена и дальнейшая работа по запуску Xorg в jail ничем не отличается от обычных шагов:

  • Установка ПО
  • Генерация /etc/X11/xorg.conf (если необходимо) и создание ~/.xinitrc
  • Запуск X

Внимание: Опция allow_kmem нарушает концепт безопасности jail и должна использоваться для контейнеризации ТОЛЬКО ВАШИХ X-клеток. Не включайте данную особенность в клетках, в которые имеют доступ потенциальные злоумышленники.

Короткое демо по запуску XJail с нуля.

Практический кейс: desktop everywhere

Хочется сделать небольшое отстутпление от темы и пофантазировать на тему будущих рабочих окружений и в частности, о моих ожиданиях по услугах хостинг-компаний ;-)

Аналитики давно отмечают, что продажи обычных стационарных компьютеров падают, наступила эра мобильности и идея о том, что ПК должен быть на каждом рабочем месте и в каждом доме теперь не кажется такой уж гениальной.

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

  • просмотр фильмов ;)
  • офисные приложения: libreoffice, vim, kmail, gimp, yed, kdevelop, valgrind и прочий стандартный набор обычных домохозяек
  • интернет броузер

При подсчете выяснилось, что цена приобретения нового системного блока удовлетворяющего этим потребностям эквивалентна цене за аренду на 3-4 года сервера в датацентре с гораздо более внушительными мощностями, что конечно, породило мысль сделать рабочую систему в Датацентре, а через RDP/VNC подключатся к рабочему столу за приложениями, не требующими большой видеонагрузки, на что дома сгодится любой современный аналог Raspberry PI.

При этом не учитывались плюсы от отсутствие шума в квартире, экономии на электричестве и то, что сервер в датацентре доступен 24×7x365, на котором параллельно можно развернуть пару jail с вспомогательными сервисами.

К слову сказать, идее терминалов не один десяток лет и внутри интранет многих компаний она давно и с успехом здравствует. Но с ростом скоростей и качества интернета, уменьшением latency, вопрос подобных услуг от хостинг-компаний — дело времени. Впрочем непонятно, что мешает этим заниматься компаниям, предоставляющим последнюю милю до клиента. Пока что, автор знает лишь небольшой список компаний, продвигающих услуги VDI в своих ДатаЦентрах через выделенные каналы.

Так или иначе, если идея выноса рабочего окружения в облако еще нежизнеспособна, то сделать какой-то гибрид на пути к этой идее можно попробовать сейчас.

К примеру, у автора статьи есть рабочий домашний компьютер с одним монитором и NVidia картой. Также, есть ноутбук с Intel HD 3000. И есть рабочий компьютер в офисе, также на NVidia карте, но с двумя мониторами. Кроме того, есть RaspberryPI. Основная ОС на всех системах — FreeBSD 10/11.

Наша задача — получить плавающее Desktop-окружение.

Если при конфигурации всего двух систем (всегда включенных) достаточно лишь при остановке jail автоматически инициировать rsync или zrep на партнера, то с множественными системами (которые не всегда online) понадобится какой-то централизованный сервер: например, можно заказать недорогой и всегда доступный VPS. Назовем его сервер снапшотов.

Далее, создается Xjail с нашим обычным окружением (например KDE4, libreoffice, audacious, xbmc, chrome, firefox и тд.) с вашей учетной записью, после чего остается решить 2 задачи:

  • Обеспечить синхронизацию данных между всеми системами;
  • Учесть разницу в периферии между теми системами, на которых вы пранируете запускать jail;

Первый пункт можно решить несколькими способами. Идеально, если все системы имеют файловую систему ZFS, которая позволяет не делая никаких паразитных нагрузок по сканированию модификаций как csync2 или rsync выполнять zfs send и получать некий TimeMachine сервис, когда можно вернуться на любой из снапшотов. Ради интереса, усложним пример и договоримся, что наши клиенты на файловой системе отличной от ZFS, но при этом функции TimeMachine хотелось бы оставить.. В этом случае, ZFS ставим только на сервер снапшотов. Для приема и отправки данных будем использовать rsync, где каждая сессия будет порождать создание ZFS snapshot на сервере снапшотов. Для этого, помещаем в rsyncd.conf следующую строчку:

			...
			pre-xfer exec = /root/bin/netsnap_rotate zroot/xjail1
			...
			

Где — zroot — общий пул, xjail1 — dataset для клетки с имемем xjail1, которая и будет нашим окружением. Скрипт /root/bin/netsnap_rotate будет создавать при каждом вызове новый снапшот. Кроме этого, чтобы количество снапшотов не было бесконечным, этот скрипт будет заниматься их ротацией. Тогда, /root/bin/netsnap_rotate может выглядеть так:

				#!/bin/sh
				set -e
				IFS="   "
				OLDESTDT=`date +%s`
				OLDESTSNAP="NOP"

				# cmp for oldest timestamp
				oldest()
				{
					if [ $2 -lt $OLDESTDT ]; then
						OLDESTDT=$2
						OLDESTSNAP=$1
					fi
				}
				
				if [ -z "$1" ]; then {
					echo "Empty FS"
					exit 1
				}
				
				OLDESTSNAP=$(zfs list -H -r -t snapshot -o name,creation $1 2>/dev/null|while read snap time; do
							dtstamp=`date -j -f "%a %b %d %H:%M %Y" "${time}" "+%s"`
							oldest $snap $dtstamp
							echo "$OLDESTSNAP"
						done)
				
				LAST=`echo $OLDESTSNAP|tail -n1`
				[ "${LAST}" = "NOP" -o -z "${OLDESTSNAP}" ] && exit
				# echo "re-created snapshot: $LAST on `date`" |mail -s "Snapshot rotated" operator@my.domain
				zfs destroy ${LAST}                                                                                                                                                                                                                                                            
				zfs snapshot ${LAST}
			

где OLDESTSNAP устанавливаем в то количество снапшотов, которое хотим хранить.

  • Вторая задача по учету разницы конфигураций решается еще проще. При первом запуске X -configure на каждой из систем, сохраняем xorg.conf за пределами jail, например в $workdir/jails-system/xjail1/hostname_xorg.conf, а в настройках клетки и параметре exec_master_prestart, которая отрабатывает в мастер хосте перед запуском клетки, просто копируем нужную конфигу в /etc/X11/xorg.conf клетки. Таким образом, как бы не отличались хост-машины графическими драйверами, настройками и количеством мониторов, система всегда будет запускаться с правильными и оптимальными настройками для активной физической системы. Некоторые WM могут запоминать геометрию и координаты окон при последнем запуске и стараться восстанавливать на прежнем месте. Если это является проблемой, достаточно найти файлы отвечающие за этот кеш и копировать их таким же образом, но как правило, все WM имеют на этот счет возможность выключить эту особенность или понимают, что геометрия окна изменилась.

    Кроме этого, на VPS/сервере снапшотов мы можем запустить внутри jail VNC/NX/RDP сервер для соединений, когда большая видеоактивность нам не нужна. Это позволит соединиться в наш рабочий стол с любых отличных от FreeBSD систем, а также пользоваться тонким клиентом с каких-нибудь low power ARM-устройств, которые сейчас можно достать из любого пылесоса и на которых Linux/FreeBSD/Android работают вполне терпимо.

    Также, нам останется в exec_master_poststop вписать команду, которая запустит rsync для отправки данных на сервер, а в exec_master_prestart — rsync для их получения. Другими словами, когда вы завершаете работу с jail, все модификации отправляются на сервер, при запуске — принимаются с него. В случае с ZFS, крайне удобно ставить zfs send в crontab, чтобы она отправляла модификации постоянно, тогда финальная сихронизация при остановке jail займет пару секунд. В случае с rsync/csync2 это делать не очень желательно, ввиду паразитной I/O нагрузки при сканировании модификаций.

    Также, если у вас есть какие-то объемные данные, которые должны быть доступны в клетке при работе на одной из WKS, но совершенно не нужны (ввиду объемов) или даже нежелательны при работе на других, удобно использовать fstab.local, через который при запуске клетки на хосте N будут примонтированы дополнительные файловые системы из мастер-хоста. К примеру, на домашней машине у вас есть склад media-материалов (видео, музыка, образы виртуальных машин VirtualBox), которые совершенно не хочется реплицировать и они просто не нужны при работе на других станциях. В таком случае, файл $workdir/jails-fstab/xjail1.fstab.local на одной из систем может иметь запись:

    				/usr/media /usr/media nullfs rw 0 0
    			

    которая монтирует /usr/media директорию из мастер-ноды в jail, однако в ZFS репликации он учавствовать не будет, поскольку это отдельный dataset (для rsync достаточно добавить --exclude)

    Подводя итоги: с помощью небольших телодвижений по связке FreeBSD, ZFS, zrep (или rsync), cbsd и Xjail мы получаем плавающее рабочее окружение, не страдающее от разницы в периферии рабочих станций, без потери в производительности как при случае виртуальных машин и не требующая постоянное ручное переконфигурирование, получаем резервное копирование в виде избыточности копий на N системах и защиту от rm -rf / в виде TimeMachine-like особенностей ZFS-снапшотов. Окружение может быть доступно по VNC, однако ввиду неготовности сегодняшних каналов обеспечивать качественную связь в современных реалиях, предполагаем что при 99% времени всей работы, задействована именно локальная графическая/media подсистема.

    Внимание: Будьте аккуратны с идеями выноса рабочего окружения к NoName оператору и всегда пользуйтесь криптованием файловых систем.

    АЛСО: в подобной схеме, вместо JAIL может выступать любой образ виртуальной машины на гипервизоре (например, хостинг Linux в bhyve или Windows в VBOX) предоставляя возможность делать снапшоты тех файловых систем внутри гвестов, которые о возможностях снапшотов даже и не мечтают. Однако это тема уже другой, не связанной с Xorg-in-jail статьи.

    HowTo с нуля по созданию клетки KDE4 с NVIDIA GL

    Для пользователей с Radeon/Intel графикой шаги аналогичны, кроме установки nvidia-settings, nvidia-driver и /usr/src

    Исходное состояние - установленная с нуля FreeBSD, без портов и дерева исходных кодов системы

    1) Устанавливаем CBSD и инициализируем рабочий каталог:

    % pkg install cbsd
    % env workdir="/usr/jails" /usr/local/cbsd/sudoexec/initenv
    % service cbsdd start
    	

    2) Нам понадобиться мышь, включим сервис moused:

    % sysrc moused_enable="YES"
    	

    3) Создаем jail с именем kde4. В подменю pkglist выберем MANUAL и напишем, какой софт нам необходим в клетке: x11/xorg x11/kde4:

    Остальной софт и параметры - на ваше усмотрение.

    4) Нам понадобиться ядро с патчами. Вы можете собрать свое по инструкции FreeBSD maillist либо получить из репозитория CBSD:

    % cbsd repo action=get sources=kernel
    	

    5) Перезапишем полученное ядро в ваш каталог /boot/kernel:

    % cbsd upgrade target=kernel
    	

    6) Перезагружаем систему на новое ядро:

    % shutdown -r now
    	

    7) В параметрах jail разрешим allow_kmem и изменим devfs_ruleset на любой не существующий (например 99). В этом случае, будет открыт весь /dev. Если вам все открывать ненадо - воспользуйтесь информацией выше как создать свой ruleset:

    % cbsd jstop kde4
    % cbsd jset jname=kde4 allow_kmem=1
    % cbsd jset jname=kde4 devfs_ruleset=99
    	
    кроме этого (только для NVIDIA пользователей) попросим CBSD примонтировать /usr/src в jail. Это необходимо для сборки nvidia-driver порта (от которого нужен libGL.so в окружении)
    % cbsd jset jname=kde4 mount_src=1
    	

    Вместо jset можно использовать TUI конфигуратор: cbsd jconfig jname=kde4

    8) Только для NVIDIA пользователей: нам понадобиться дерево исходных кодов в /usr/src для сборки x11/nvidia-drivers

    Для этого, наполните /usr/src и /usr/ports соответствующим содержимим. Либо, можно попросить это сделать CBSD

    Для наполнения /usr/ports из SVN FreeBSD:

    % cbsd portsup
    	

    Для наполнения $workdir/src/src\* (на который мы создадим симлинк чтобы не держать две копии) из SVN FreeBSD:

    % cbsd srcup
    % rmdir /usr/src ( либо на ZFS инсталляциях, удалить псевдо-fs: zfs destroy zroot/usr/src )
    % ln -sf ~cbsd/src/src_10.1/src /usr/src
    	

    (Если версия FreeBSD не 10.1, путь будет другим)

    9) Только для NVIDIA пользователей: установим зависимости nvidia-driver из репозитория установкой и удалением nvidia-driver. Кроме этого, нам понадобится glproto

    % pkg install x11/glproto x11/nvidia-driver 
    % pkg remove nvidia-driver
    	

    10) Только для NVIDIA пользователей: Устанавливаем драйвер NVIDIA из портов

    	env BATCH=yes make -C /usr/ports/x11/nvidia-driver install
    	

    11) Только для NVIDIA пользователей: Прописываем модуль nvidia.ko в автозагрузку, увеличиваем ipc.shmall как советует порт:

    % sysrc kld_list="nvidia"
    % echo "kern.ipc.shmall=1310720" >> /etc/sysctl.conf
    	

    11) Те же действия, но для пользователей с графикой от Intel. Добавить в /boot/loader.conf:

    	hw.vga.textmode=1                                                                                                                                                                                                                                                              
    	drm_load="YES"                                                                                                                                                                                                                                                                 
    	drm2_load="YES"                                                                                                                                                                                                                                                                
    	i915kms_load="YES"
    	

    12) Перезагружаем сервер (или подгружаем модули вручную), запускаем и подготавливаем клетку

    % shutdown -r now
    % cbsd jstart kde4
    % cbsd jlogin kde4
    	

    13) Последующие шаги выполняются уже в окружении. Внутри jail выставим dbus в автозагрузку

    % sysrc dbus_enable="YES"
    % service dbus start
    	

    14) Устанавливаем в jail nvidia-driver по аналогии с пунктом 9 и 10

    % pkg install x11/glproto x11/nvidia-driver 
    % pkg remove nvidia-driver
    % env BATCH=yes make -C /usr/ports/x11/nvidia-driver install
    

    15) Если не все еще умеете писать рабочий xorg.conf с нуля, лучше сгенерировать его через nvidia-xconfig:

    % pkg install x11/nvidia-xconfig
    % nvidia-xconfig
    	

    Пользователи с Intel могут воспользоваться штатным X:

    % X -configure
    	

    И скопировать сгенерированный /root/xorg.conf.new в /etc/X11/xorg.conf

    Допишем в сгенерированный /etc/X11/xorg.conf секцию ServerFlags:

    Section "ServerFlags"
           Option         "AutoAddDevices" "off"
           Option         "AutoEnableDevices" "off"
           Option         "AllowEmptyInput" "off"
    EndSection
    

    16) Создаем пользователя xuser, если вы этого не сделали при создании jail:

    % pw useradd xuser -m -s /bin/csh
    	

    17) Заходим пользователем, пишем стандартный ~/.xinitrc и запускаем KDE:

    % su - xuser
    % echo startkde >> ~/.xinitrc
    % xinit
    	

    Возможно, будет полезно провести дополнительные настройки

    18) Примонтировать Linux-специфичные FS, для нормальной работы Linux-приложений в клетке. Для этого в мастер ноде создадим локальный fstab для клетки с таким содержимым:

    файл /usr/jails/jails-fstab/fstab.kde4.local

    	linprocfs       /usr/compat/linux/proc  linprocfs       rw      0       0
    	

    19) Если вы планируете синхронизировать этот jail с другим сервером и периодически работать на другой ноде, при этом конфигурация второй ноды отличается, лучше всего сохранить xorg.conf в мастер-ноду и создать скрипт, который будет копировать его каждый раз в /etc/X11/ клетки. Тоже самое необходимо будет сделать на другой ноде

    Делается в мастер ноде

    % cp /usr/jails/jails-data/kde4-data/etc/X11/xorg.conf /etc/X11
    

    в каталоге /usr/jails/jails-system/kde4/master_poststart.d создать скрипт xorg.sh с содержимым:

    #!/bin/sh
    cp -a /etc/X11/xorg.conf ${data}/etc/X11/xorg.conf
    

    Сделать файл исполняемым:

    % chmod +x /usr/jails/jails-system/kde4/master_poststart.d/xorg.sh
    

    Также можно поступить с /etc/resolv.conf и /etc/hosts при надобности