Работа с контейнерами во FreeBSD (Jails)

С момента выхода предыдущей статьи о системе контейнеров во FreeBSD прошло более 10 лет и с тех пор она была существенно доработана и усовершенствована. Так появились возможности настройки клеток посредством отдельных конфигурационных файлов, обработки событий создания и уничтожения клеток, существенно увеличено количество настраиваемых параметров. Одним из самых заметных изменений в системе клеток является реализация виртуализации сетевого стека (VNET/VIMAGE), позволившая создавать клетку с изолированным от основной системы сетевым стеком, в котором может работать даже брандмауэр. После этого в версии фряхи 13.3 в клетке с изолированным сетевым стеком стало возможным запускать сервер NFS.

В одной из небольших сетей мне понадобилось настроить файлопомойку с применением программного обеспечения Samba. В случае запуска Samba в клетке без включенной виртуализации сетевого стека в логи полезут ошибки о невозможности рассылки широковещательных пакетов через интерфейс (конечно можно добавить в конфиг nmbd bind explicit broadcast = no, чтобы отключить рассылку широковещательных пакетов). Использование технологии VNET позволит выделить клетке отдельный интерфейс, по которому будет возможна отправка широковещательных пакетов. Кроме этого данный интерфейс можно привязать к заданной VLAN.

Все действия я буду производить на FreeBSD версии 13.2. Для начала нам необходимо подготовить окружение, для этого скачиваем дистрибутив фряхи и распаковываем его в место будущего расположения клетки.

# mkdir -p /home/jails/samba
# cd /home/jails
# fetch https://download.freebsd.org/ftp/releases/amd64/13.2-RELEASE/base.txz
# tar -xvf /home/jails/base.txz -C /home/jails/samba

P.S. Отмечу, что у меня директория /home является символической ссылкой на /usr/home.

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

# chroot /home/jails/samba
# tzsetup
# cd /etc/mail && make aliases
# touch /etc/fstab
# echo 'nameserver your.ip' > /etc/resolv.conf

Помимо этого в новом окружении в файле /etc/rc.conf можно задать сетевые параметры и настроить запуск приложений, например так:

hostname="samba.local"
rpcbind_enable="NO"     # отключаем сервис rpcbind
syslogd_flags="-ss"     # с данным флагом демон syslogd не будет создавать сетевой сокет
inetd_enable="YES"      # для запуска демона sshd, ftpd и иных сервисов
inetd_flags="-wW -C 60 -a ip.address"

После настройки окружения клетки переходим к правке файлов в основной системе. В моей конфигурации клетке нужно работать в сети с заданным VLAN равным 7, поэтому я в основной системе в /etc/rc.conf через директиву vlans_ifname  задаю номер VLAN для интерфейса, таким образом у меня создается новый интерфейс с именем re0.7. Согласно официальной документации и информации на форумах freebsd.org общий подход для выделения интерфейса клетке представляет собой создание bridge и epair, после чего в мост добавляются физический или виртуальный (например VLAN) интерфейс и один из "концов" epair. Другой конец epair функционирует в клетке . Настройки клетки осуществляются посредством редактирования файла /etc/jail.conf

# В общем блоке задаем глобальные параметры
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail/console_${name}.log";
exec.clean;
mount.devfs;

# Базовый путь для клеток
path = "/home/jails/$name";

samba {
	# Я привязываю клеткам индивидуальный идентификатор (мне так удобно)
	jid = 1; 
	host.hostname = "samba.local";
	# Файл с точками монтирования, которые будут подключены при создании клетки
	mount.fstab = "/etc/fstab.samba";

	allow.raw_sockets;
	allow.quotas;
	# Права на монтирование fdescfs в клетке необходимы для Samba версии новее 4.15
	enforce_statfs = "1";
	allow.mount;
	allow.mount.fdescfs;

	# Параметры VLAN интерфейса в системе
	$vlan_id = "7";
	$vlan_if = "re0.7";
	$vlan_ip = "192.168.1.240/24";
	$gateway = "192.168.1.250";

	# Настройки VNET. Первой директивой включаем виртуализацию,
	# второй - указываем виртуальный либо физический интерфейс, 
	# который будет доступен в клетке для работы
	vnet;
	vnet.interface = "epair${jid}b";

	# Для придания уникальности именам интефейсов будем использовать
	# идентификатор клетки
	exec.prepare += "ifconfig epair${jid} create";
	exec.prepare += "ifconfig bridge${jid} create";

	exec.prestart += "ifconfig ${vlan_if} up";
	exec.prestart += "ifconfig epair${jid}a up descr 'jail ${name} side of the epair'";
	exec.prestart += "ifconfig bridge${jid} up addm ${vlan_if} addm epair${jid}a descr 'bridge for the jail ${name}'";

	# Так как указанный в vnet.interface интерфейс после старта клетки будет передан ей в управление,
	# можно задать IP адрес и другие необходимые параметры
	exec.start += "ifconfig epair${jid}b ${vlan_ip} up descr 'jail side of the epair only visible in the jail'";

	# Устанавливаем шлюз по умолчанию
	exec.start += "route add default ${gateway}";

	# На этапе остановки клетки необходимо удалить дочерние интерфейсы,
	# чтобы в дальнейшем при повторном запуске клетки не возникло ошибок
	exec.poststop += "ifconfig bridge${jid} deletem epair${jid}a deletem ${vlan_if}";
	# Отмечу, что в случае удаления любого конца epair, второй удалится автоматически
	exec.poststop += "ifconfig epair${jid}a destroy";
	exec.poststop += "ifconfig bridge${jid} destroy";
}

P.S. Обновив систему FreeBSD до версии 14.0 столкнулся с проблемой, вызванной низкой скоростью записи (вплоть до 0 Mbps) данных в клетке. Проблема проявилась как при записи файлов через SMB так и через FTP. При этом в основной системе скорость записи файлов через тот же FTP была на приемлемом уровне, т.е. скорость снижалась где-то в связке bridge и epair. В ходе экспериментов и поиска информации в интернете установлено, что отключение на основном физическом интерфейсе (на котором созданы VLAN и др.) Large Receive Offload (LRO) решает данную проблему.

Для настройки клетки можно задать следующие параметры (список не исчерпывающий и необходимо учитывать, что в старых версиях FreeBSD некоторые из них могут быть не доступны):

  • ip4 - может принимать значения:
    • inherit - использовать IP адрес основной системы;
    • new - привязать к клетке новый IP адрес с маской подсети, заданные в ip4.addr;
    • disable - не использовать сетевой протокол IPv4.
  • ip4.addr - IP адрес с маской подсети в формате IP/NETMASK.
  • ip6 и ip6.addr - смысл такой же как и у директив ip4, ip4.addr, только для протокола IPv6.
  • vnet - создать клетку с собственным виртуальным сетевым стеком (собственные сетевые интерфейсы, адреса, таблица маршрутизации и т.д.). При этом ядро должно быть скомпилировано с параметром VIMAGE. Возможные значения inherit, тогда будет использован сетевой стек основной системы, а также new, тогда будет создан новый сетевой стек.
  • vnet.interface - интерфейс, который будет доступен в клетке с включенной виртуальным стеком.
  • devfs_ruleset - номер правила devfs, применяемое при монтировании данной ФС в клетке. Нулевое значение (по умолчанию) означает отсутствие набора правил. Правила devfs наследуются в дочерних клетках. Монтирование devfs внутри клетки возможно при наличии разрешения allow.mount, allow.mount.devfs и установки enforce_statfs в значение меньше 2. При этом правила devfs нельзя просмотреть или изменить из клетки.
  • mount - подключаемые файловые системы в клетку при ее создании (формат как в fstab).
  • mount.fstab - путь к файлу с точками монтирования, которые будут подключены при создании клетки.
  • mount.fdescfs - в созданной клетке будет подключена fdescfs.
  • mount.procfs - в созданной клетке будет подключена procfs.
  • persist - при установке этого параметра возможно создать клетку без работающих процессов.
  • allow.set_hostname - разрешить изменение имени хоста в клетке.
  • allow.raw_sockets - разрешить в клетке поточные сокеты (используются в утилитах ping, traceroute и т.п.).
  • allow.chflags - без этого параметра все пользователи, в т.ч. привилегированные, не смогут менять флаги chflags.
  • allow.mount.devfs -  привилегированные пользователи внутри клетки смогут подключать и отключать файловую систему devfs. Данная опция работает только при установленной директиве allow.mount и установленном параметре enforce_statfs меньше 2. Желательно задавать ограничение через devfs_ruleset.
  • allow.mount.fdescfs, allow.mount.fusefs, allow.mount.nullfs, allow.mount.procfs, allow.mount.linprocfs, allow.mount.linsysfs, allow.mount.tmpfs, allow.mount.zfs - привилегированные пользователи внутри клетки смогут подключать и отключать указанные файловые системы fdescfs, fusefs, nullfs, procfs, linprocfs, linsysfs, tmpfs, zfs. Данные опции работают только при установленной директиве allow.mount и установленном параметре enforce_statfs меньше 2.
  • allow.quotas - разрешить управлять квотами в клетке.
  • allow.read_msgbuf - разрешить доступ к сообщениям ядра в клетке.
  • allow.reserved_ports - разрешить в клетке открыть слушающие сокеты на портах ниже 1024.
  • sysvmsg - разрешить в клетке работать с сообщениями System V IPC.
  • sysvsem, sysvshm - разрешить в клетке работать с механизмами межпроцессного взаимодействия System V IPC (семафоры и разделяемая память).
  • depend - список клеток от которых зависит клетка, то есть которые будут запущены первыми.
  • allow.vmm - клетка может получить доступ к vmm. Данный  флаг доступен когда модуль ядра vmm загружен.
  • exec.fib - запускать команды с указанным номером таблицы маршрутизации FIB.
  • exec.prepare, exec.prestart и т.п. - набор директив, которые позволяют выполнять команды оболочки на различных стадиях создания, запуска, работы и уничтожения клетки.

P. S. Поскольку есть не очевидные моменты, для примера приведу правила из /etc/devfs.rules, которые в клетку пробрасывают zvol для раздачи через iSCSI.

[devfsrules_jail_zvol=11]
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add include $devfsrules_jail
add path 'cam' unhide
add path 'cam/ctl' unhide
add path 'zvol' unhide
add path 'zvol/over8k' unhide
add path 'zvol/over8k/volgames*' unhide

Здесь стоит обратить внимание на то, что подключаемые команды из других секций не наследуются рекурсивно. То есть несмотря на то, что в devfsrules_jail есть add include $devfsrules_hide_all, правила из него наследоваться не будут. Также важным моментом является то, что нужно открывать доступ не только к конечному устройству, а также к директории его содержащей. Ну и после изменения правил devfs незабываем их обновить через service devfs restart.

Предварительная настройка клетки закончена и чтобы клетка автоматом запускалась при старте системы нужно добавить несколько строк в /etc/rc.conf:

jail_enable="YES"
jail_parallel_start="YES"
jail_list="samba"
  • jail_enable - разрешить запуск клеток;
  • jail_list - список запускаемых при старте системы клеток, разделенных пробелами;
  • jail_parallel_start - запускать клетки в фоне.

Теперь можно запускать клетку. Вот как можно запустить/остановить отдельную клетку:

# service jail start samba
# service jail stop samba

А так можно запустить/остановить все клетки:

# service jail start
# service jail stop

Просмотреть список запущенных клеток можно командой jls:

# jls
  JID  IP Address      Hostname                      Path
    1                  samba.local                   /usr/home/jails/samba

У клеток, в которых создан виртуальный сетевой стек, в выводе команды jls IP адрес обычно не отображается. Запустить оболочку в запущенной клетке можно так:

# jexec -u root samba csh

После этого можно установить необходимые пакеты в систему, в моем случае Samba. Настройку Samba я здесь описывать не буду, т.к. это тема для отдельной статьи. Кроме того, на сайте есть несколько материалов по данной тематике, которые можно найти, воспользовавшись поиском. Со временем разработчики FreeBSD выпускают обновления и заплатки безопасности, которые я устанавливаю командой freebsd-update (папку с окружением клетки можно задать через ключ -b).

Отмечу, что в настоящее время создано множество утилит, которые упрощают управление клетками (создание, обновление, удаление, резервное копирование и т.п.), в частности к таким можно отнести bastille, cbsd, appjail и другие. Рекомендую почитать раздел про клетки в хэндбуке, в котором неплохо раскрыт вопрос управления клетками, в частности использования файловых систем nullfs и zfs для создания одного бинарного окружения, используемого несколькими клетками.

Добавить комментарий

CAPTCHA
Этот вопрос задается для того, чтобы выяснить, являетесь ли Вы человеком или представляете из себя автоматическую спам-рассылку.
Яндекс.Метрика