---
description: 'Клетки (jails) улучшают концепцию традиционного окружения chroot несколькими способами'
next: books/handbook/containers
params:
  path: /books/handbook/jails/
part: 'Часть III. Администрирование системы'
prev: books/handbook/security
showBookMenu: true
tags: ["jails", "creating", "managing", "updating"]
title: 'Глава 17. Клетки и контейнеры'
weight: 21
---

[[jails]]
= Клетки и контейнеры
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumoffset: 17
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/handbook/jails/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

[[jails-synopsis]]
== Обзор

Поскольку администрирование системы — сложная задача, было разработано множество инструментов, чтобы облегчить жизнь администратору. Эти инструменты часто улучшают способы установки, настройки и обслуживания систем. Один из инструментов, который можно использовать для повышения безопасности системы FreeBSD, — это _клетки (jails)_. Клетки доступны начиная с FreeBSD 4.X и продолжают совершенствоваться в плане полезности, производительности, надёжности и безопасности.

Клетки расширяют концепцию man:chroot[2], которая используется для изменения корневого каталога для набора процессов. Это создаёт безопасное окружение, изолированное от остальной системы. Процессы, созданные в окружении chroot, не могут обращаться к файлам или ресурсам за его пределами. По этой причине компрометация службы, работающей в окружении chroot, не должна позволить злоумышленнику скомпрометировать всю систему.

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

Клетки улучшают концепцию традиционной изолированной среды chroot несколькими способами.

В традиционной среде chroot процессы ограничены только в части файловой системы, к которой они могут получить доступ. Остальные системные ресурсы, пользователи системы, запущенные процессы и подсистема сети разделяются между процессами в chroot и процессами основной системы. Клетки расширяют эту модель, виртуализируя доступ к файловой системе, набору пользователей и подсистеме сети. Доступны более детальные настройки для регулирования доступа в изолированной среде. Клетки можно рассматривать как один из видов виртуализации на уровне операционной системы.

Эта глава охватывает:

* Что такое клетка и для каких целей он может использоваться в FreeBSD.
* Типы клеток.
* Различные способы настройки сети для клетки.
* Файл конфигурации клетки.
* Как создать различные типы клетки.
* Как настроить трансляцию сетевых адресов для клеток.
* Как запустить, остановить и перезапустить клетку.
* Основы администрирования клеток, как изнутри, так и снаружи клетки.
* Как обновить различные типы клеток.
* Как ограничить ресурсы, которые может использовать клетка.
* Дополнительные параметры безопасности и функций на уровне клетки.
* Как делегировать клетке наборы данных ZFS.
* Неполный список различных менеджеров клеток FreeBSD.

[[jail-types]]
== Типы клеток

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

Ниже приведён список различных типов, их характеристики и рекомендации по использованию.

[[thick-jails]]
=== Толстые клетки (Thick Jails)

Толстая клетка (thick jail) — это традиционная форма клетки FreeBSD. В толстой клетке полная копия базовой системы реплицируется внутри окружения клетки. Это означает, что клетка имеет свою собственную отдельную копию базовой системы FreeBSD, включая библиотеки, исполняемые файлы и конфигурационные файлы. Клетку можно рассматривать как почти полноценную автономную установку FreeBSD, но работающую в рамках хост-системы. Такая изоляция гарантирует, что процессы внутри клетки остаются отделёнными от процессов на хосте и в других клетках.

Преимущества толстых клеток:

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

Недостатки толстых клеток:

* Ресурсные затраты: поскольку каждая клетка поддерживает свою собственную отдельную базовую систему, толстые клетки потребляют больше ресурсов по сравнению с тонкими клетками.
* Обслуживание: каждая клетка требует собственного обслуживания и обновлений для своих базовых системных компонентов.

[[thin-jails]]
=== Тонкие клетки (Thin Jails)

Тонкая клетка использует базовую систему из шаблона, используя либо снимки OpenZFS, либо монтирования NullFS. Для каждой тонкой клетки дублируется лишь минимальный поднабор базовой системы, что приводит к меньшему потреблению ресурсов по сравнению с толстой клеткой. Два механизма ведут себя по-разному после создания клеток. При методе OpenZFS каждая тонкая клетка создается как клон снимка шаблона: независимая копия с записью при копировании, которая разделяет неизмененные блоки с шаблоном, но расходится при изменении. Более поздние изменения шаблона не распространяются на существующие клоны, и каждый клон обслуживается и обновляется индивидуально, во многом как толстая клетка. При методе NullFS каждая тонкая клетка монтирует шаблон только для чтения во время выполнения, поэтому базовая система используется совместно в реальном времени, и обновление шаблона обновляет каждую клетку, которая его монтирует. Эта общая база, доступная только для чтения, также обеспечивает тонким клеткам NullFS меньшую изоляцию и независимость, чем толстым клеткам, поскольку изменения в общих компонентах одновременно затрагивают каждую клетку, использующую их.

Вкратце, тонкая клетка в FreeBSD — это тип клетки FreeBSD, который воспроизводит значительную часть, но не всю базовую систему, в изолированной среде.

Преимущества тонких клеток:

* Эффективность использования ресурсов: тонкие клетки более эффективны в использовании ресурсов по сравнению с толстыми клетками. Поскольку они используют общую базовую систему, они занимают меньше места на диске и оперативной памяти. Это позволяет запускать больше клеток на том же оборудовании без чрезмерного потребления ресурсов.
* Быстрое развёртывание: создание и запуск тонких клеток обычно происходит быстрее по сравнению с толстыми клетками. Это может быть особенно полезно при быстром развертывании множества экземпляров.
* Унифицированное обслуживание: используя метод NullFS, когда тонкие клетки используют одну копию базовой системы, доступную для чтения, обновления и обслуживание общих компонентов базовой системы (таких как библиотеки и исполняемые файлы) необходимо выполнять только один раз на шаблоне. Это упрощает процесс обслуживания по сравнению с поддержкой отдельной базовой системы для каждой толстой клетки. Тонкие клетки в клоне OpenZFS не обладают таким преимуществом: так как каждый клон независим, обновление должно быть применено к каждой клетке индивидуально, как и в случае с толстыми клетками.
* Общие ресурсы: тонкие клетки могут проще разделять общие ресурсы, такие как библиотеки и исполняемые файлы, с хост-системой. Это может привести к более эффективному использованию кэширования диска и повышению производительности приложений внутри клетки.

Недостатки тонких клеток:

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

[[service-jails]]
=== Сервисные клетки (Service Jails)

Сервисная клетка (service jail) напрямую разделяет всё дерево файловой системы с хостом (корневой путь клетки — [.filename]#/#) и, таким образом, может получать доступ и изменять любые файлы на хосте, а также использует те же учётные записи пользователей, что и хост. По умолчанию у неё нет доступа к сети или другим ресурсам, ограниченным в клетках, но её можно настроить для повторного использования сети хоста и снятия некоторых ограничений клетки. Основное применение сервисных клеток — автоматическое ограничение служб/демонов внутри клетки с минимальной настройкой и без необходимости знания файлов, требуемых такой службой/демоном. Сервисные клетки доступны с версии FreeBSD 15.0.

Преимущества сервисных клеток:

* Нулевое администрирование: для службы, готовой к использованию в клетке, достаточно одной строки конфигурации в [.filename]#/etc/rc.conf#, для службы, не готовой к использованию в клетке, требуется две строки конфигурации.
* Ресурсоэффективность: сервисные клетки более эффективны в использовании ресурсов, чем тонкие клетки, так как они не требуют дополнительного дискового пространства или сетевых ресурсов.
* Быстрое развёртывание: создание и запуск сервисных клеток обычно выполняется быстрее по сравнению с тонкими клетками, если требуется изолировать только отдельные сервисы/демоны и не нужны параллельные экземпляры одного и того же сервиса/демона.
* Общие ресурсы: сервисные клетки разделяют все ресурсы, такие как библиотеки и исполняемые файлы, с хост-системой. Это может привести к более эффективному использованию кэширования диска и повышению производительности приложений внутри клетки.
* Изоляция процессов: сервисные клетки изолируют определённую службу, она не может видеть процессы, которые не являются дочерними по отношению к этой сервисной клетке, даже если они выполняются в рамках той же учётной записи пользователя.

Недостатки сервисных клеток:

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

Большая часть настройки клеток, обсуждаемая ниже, не требуется для сервисных клеток. Чтобы понять, как работают клетки, рекомендуется разобраться в этих возможностях конфигурации. Подробности о том, что необходимо для настройки сервисной клетки, приведены в разделе crossref:jails[service-jails-config, Настройка сервисных клеток].

[[vnet-jails]]
=== Клетки VNET

Клетка FreeBSD VNET — это виртуализированная среда, которая обеспечивает изоляцию и контроль сетевых ресурсов для процессов, выполняющихся внутри неё. Она предоставляет высокий уровень сетевой сегментации и безопасности, создавая отдельный сетевой стек для процессов внутри клетки, что гарантирует изоляцию сетевого трафика внутри клетки от хост-системы и других клеток.

По сути, механизм VNET в FreeBSD добавляет возможность настройки сети. Это означает, что клетку VNET можно создать как толстой, так и тонкой.

[[linux-jails]]
=== Клетки Linux

Клетки Linux в FreeBSD — это функция в операционной системе FreeBSD, которая позволяет использовать исполняемые файлы Linux и приложения внутри клетки FreeBSD. Эта функциональность достигается за счёт совместимости, которая позволяет транслировать и выполнять определённые системные вызовы Linux и использовать библиотеки Linux с ядром FreeBSD. Цель клеток Linux — облегчить выполнение Linux-программ в системе FreeBSD без необходимости в отдельной виртуальной машине или среде Linux.

[[host-configuration]]
== Конфигурация хоста

Прежде чем создавать клетку на хост-системе, необходимо выполнить определённые настройки и получить некоторую информацию от хост-системы.

Потребуется настроить утилиту man:jail[8], создать необходимые каталоги для настройки и установки клетки, получить информацию о сети хоста и проверить, использует ли хост файловую систему OpenZFS или UFS.

[TIP]
====
Версия FreeBSD, работающая в клетке, не может быть новее версии, работающей на хосте.
====

[[host-configuration-jail-utility]]
=== Утилита jail

Утилита man:jail[8] управляет клетками.

Чтобы запускать клетки при загрузке системы, выполните следующие команды:

[source, shell]
....
# sysrc jail_enable="YES"
# sysrc jail_parallel_start="YES"
....

[TIP]
====
С `jail_parallel_start` все настроенные клетки будут запущены в фоновом режиме.
====

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

[source, shell]
....
# sysrc jail_list="www db"
....

Имена в `jail_list` разделяются пробелами и должны соответствовать именам клеток, определённым в [.filename]#/etc/jail.conf# или [.filename]#/etc/jail.conf.d/#. При загрузке запускаются только перечисленные клетки, и они запускаются в указанном порядке, поэтому любую клетку, пропущенную в `jail_list`, необходимо запускать вручную. Когда `jail_list` остаётся пустым, запускаются все клетки, определённые в файлах конфигурации.

Чтобы остановить клетки в обратном порядке их `jail_list` при завершении работы, установите `jail_reverse_stop`:

[source, shell]
....
# sysrc jail_reverse_stop="YES"
....

[NOTE]
====
С включённым `jail_parallel_start` клетки запускаются в фоновом режиме, и порядок `jail_list` больше не гарантируется. Когда одна клетка должна работать до запуска другой, выразите эту зависимость с помощью параметра `depend` в [.filename]#/etc/jail.conf# вместо того, чтобы полагаться на порядок списка. Зависимости клетки создаются автоматически до её запуска и останавливаются в обратном порядке, независимо от `jail_list`. Параметр `depend` описан в разделе crossref:jails[jail-configuration-files,Файлы конфигурации клеток].
====

[[jails-networking]]
=== Сетевое взаимодействие

Сеть для клеток FreeBSD может быть настроена несколькими различными способами:

Режим сетевого взаимодействия хоста (разделение IP)::
В режиме сетевого доступа хоста клетка использует сетевой стек хост-системы, а не получает собственный. Совместное использование стека не означает совместное использование адреса: распространённая настройка предоставляет клетке собственный выделенный `ip4.addr` (и `ip6.addr`), который man:jail[8] добавляет как псевдоним на интерфейсе хост-системы, поэтому клетка и хост-система отвечают на разных адресах через один и тот же стек. Только клетка, настроенная с `ip4 = inherit`, полностью видит и может пользоваться адресами хост-системы и отвечает на собственном адресе хост-системы, получая неограниченный доступ ко всем адресам, которыми владеет хост-система.

Виртуальные сети (VNET)::
Виртуальные сети (VNET) — это функция клеток FreeBSD, предоставляющая более продвинутые и гибкие сетевые решения по сравнению с базовыми режимами, такими как режим сетевого взаимодействия хоста (host networking). VNET позволяет создавать изолированные сетевые стеки для каждой клетки, предоставляя им отдельные IP-адреса, таблицы маршрутизации и сетевые интерфейсы. Это обеспечивает более высокий уровень сетевой изоляции и позволяет клеткам функционировать так, как если бы они работали на отдельных виртуальных машинах.

Система netgraph::
man:netgraph[4] — это универсальная инфраструктура ядра для создания пользовательских сетевых конфигураций. Она может использоваться для определения того, как сетевой трафик передаётся между клеткой и хост-системой, а также между различными клетками.

[[jail-ip-comes-from]]
==== Откуда берется IP-адрес клетки

Клетка с общим стеком получает доступ к сети через адрес на интерфейсе хоста. В параметре `interface` указывается имя этого интерфейса, и для каждого адреса в `ip4.addr` и `ip6.addr` man:jail[8] сам управляет псевдонимом: как выражается jail(8), «псевдоним для каждого адреса будет добавлен к интерфейсу до создания клетки и будет удалён из интерфейса после удаления клетки». Ничто на хост-системе не обязано предварительно настраивать адрес.

Интерфейс также может быть указан для каждого адреса, так что одна строка связывает адрес с интерфейсом, который должен этот адрес использовать:

[.programlisting]
....
ip4.addr = "em0|192.168.1.151/24";
....

Когда адрес должен сохраняться независимо от клетки — потому что другая служба на хост-системе полагается на него, или потому что параметр `interface` не используется — назначьте его на хост-системе как псевдоним rc.conf и исключите его из управления jail(8):

[source, shell]
....
# sysrc ifconfig_em0_aliases="inet 192.168.1.151/32"
....

Более старая, нумерованная форма `ifconfig_em0_alias0="inet 192.168.1.151/32"` по-прежнему работает, но форма `_aliases` предпочтительнее для новых конфигураций, поскольку она позволяет избежать хрупкого требования, чтобы нумерованные псевдонимы образовывали непрерывную последовательность `alias0`, `alias1`, `alias2`.

[[loopback-addresses-shared-ip-Jails]]
==== Адреса loopback в клетках с общей IP-адресацией

Клетка с общим стеком не имеет собственного интерфейса loopback. Ядро FreeBSD компенсирует это: в клетке без VNET, когда `127.0.0.1` или `::1` сами по себе не являются адресами клетки, ядро молча переписывает как `bind()`, так и `connect()` на адрес loopback на первый `ip4.addr` или `ip6.addr` клетки. Это поведение не документировано в man:jail[8], но оно важно, поскольку несет последствия для безопасности.

[WARNING]
====
Не предполагайте, что привязка службы к `localhost` ограничивает её клеткой. В клетке с общим IP-адресом демон, которому указано прослушивать `127.0.0.1`, на самом деле привязывается к первому адресу клетки, который часто обращён к локальной сети, и поэтому становится доступным из сети.
====

Стандартным средством смягчения является предоставление клетке частного адреса loopback и указание его первым. Клонируйте выделенный интерфейс loopback на хосте и назначьте ему адрес из диапазона `127.0.0.0/8` (или частный адрес RFC 1918):

[source, shell]
....
# sysrc cloned_interfaces="lo1"
# sysrc ifconfig_lo1="inet 127.0.1.1/32"
....

Затем укажите этот адрес первым в `ip4.addr` клетки, перед любым маршрутизируемым адресом, чтобы переопределение loopback попадало на частный адрес, а не на адрес, обращённый к локальной сети.

[[preparing-host-services]]
==== Подготовка сервисов хост-системы

Каждый псевдоним адреса клетки разделяет порты хост-системы. Демон хост-системы, привязанный к адресу с подстановочным знаком — показанному как `*:port` в списках прослушивателей — поэтому также прослушивает каждый псевдоним клетки, и он занимает этот порт прежде, чем собственная служба клетки сможет его заявить. Перед запуском любых клеток проверьте, что хост-система уже прослушивает:

[source, shell]
....
# sockstat -4 -6 -l
....

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

* man:syslogd[8]: задайте `syslogd_flags="-ss"` в [.filename]#/etc/rc.conf#. Одинарный `-s` уже запускает syslogd в безопасном режиме, чтобы он не прослушивал соединения с удаленных хостов для отправки сообщений журнала; согласно syslogd(8), если `-s` "указан дважды, сетевой сокет вообще не будет открыт", что также предотвращает отправку syslogd на удаленные хосты.
* man:sshd[8]: закрепите `ListenAddress` на адресе хост-системы в файле [.filename]#/etc/ssh/sshd_config# вместо того, чтобы оставлять его с подстановочным знаком(wildcard) по умолчанию.
* man:ntpd[8] и man:sendmail[8]: привяжите их к адресу хост-системы также через их собственную конфигурацию.

Для контроля на уровне межсетевого экрана за тем, какие адреса может использовать клетка, см. crossref:firewalls[firewalls,Межсетевые экраны].

[[host-configuration-directories]]
=== Настройка дерева каталогов клетки

Для файлов клеток не назначено какого-то заранее определённого места.

Некоторые администраторы используют [.filename]#/jail#, другие — [.filename]#/usr/jail#, а третьи — [.filename]#/usr/local/jails#. В этой главе будет использоваться [.filename]#/usr/local/jails#.

Помимо каталога [.filename]#/usr/local/jails# будут созданы другие каталоги:

* [.filename]#media# будет содержать сжатые файлы загруженных пользовательских окружений.
* [.filename]#templates# будет содержать шаблоны при использовании тонких клеток.
* [.filename]#containers# будет содержать клетки.

При использовании OpenZFS выполните следующие команды для создания наборов данных для этих каталогов:

[source, shell]
....
# zfs create -o mountpoint=/usr/local/jails zroot/jails
# zfs create zroot/jails/media
# zfs create zroot/jails/templates
# zfs create zroot/jails/containers
....

[TIP]
====
В данном случае для родительского набора данных использовался `zroot`, но могли быть использованы и другие наборы данных.
====

Создание отдельного дочернего набора данных для каждой клетки в дальнейшем позволяет делать снимки, клоны, квоты и делегирование наборов данных для каждой клетки. Поскольку каталог каждой клетки затем становится собственным набором данных, OpenZFS может как ограничивать, так и гарантировать использование ею дискового пространства с помощью свойств `quota` и `reservation`; см. crossref:jails[jail-resource-limits,Ограничения ресурсов клетки].

При использовании UFS выполните следующие команды для создания каталогов:

[source, shell]
....
# mkdir /usr/local/jails/
# mkdir /usr/local/jails/media
# mkdir /usr/local/jails/templates
# mkdir /usr/local/jails/containers
....

[[jail-configuration-files]]
=== Файлы конфигурации клетки

Существует два способа настройки клеток.

Первый вариант — добавить запись для каждой клетки в файл [.filename]#/etc/jail.conf#. Другой вариант — создать отдельный файл для каждой клетки в каталоге [.filename]#/etc/jail.conf.d/#.

В случае, если на хост-системе мало клеток, записи для каждого клетки можно добавить в файл [.filename]#/etc/jail.conf#. Если на хост-системе много клеток, рекомендуется создать отдельный конфигурационный файл для каждой клетки в каталоге [.filename]#/etc/jail.conf.d/#.

Файлы в [.filename]#/etc/jail.conf.d/# должны иметь расширение `.conf`, и их необходимо подключить в [.filename]#/etc/jail.conf#:

[.programlisting]
....
.include "/etc/jail.conf.d/*.conf";
....

Типичная запись с настройками клетки выглядит следующим образом:

[.programlisting]
....
jailname { <.>
  # STARTUP/LOGGING
  exec.start = "/bin/sh /etc/rc"; <.>
  exec.stop = "/bin/sh /etc/rc.shutdown"; <.>
  exec.consolelog = "/var/log/jail_console_${name}.log"; <.>

  # PERMISSIONS
  allow.raw_sockets; <.>
  exec.clean; <.>
  mount.devfs; <.>

  # HOSTNAME/PATH
  host.hostname = "${name}"; <.>
  path = "/usr/local/jails/containers/${name}"; <.>

  # NETWORK
  ip4.addr = 192.168.1.151; <.>
  ip6.addr = "2001:db8::151"; <.>
  interface = em0; <.>
}
....

<.> `jailname` — имя клетки.
<.> `exec.start` — команда(ы), выполняемые в среде клетки при её создании. Типичная команда для выполнения — "/bin/sh /etc/rc".
<.> `exec.stop` — команда(ы), выполняемые в среде клетки перед её удалением. Типичная команда для выполнения — "/bin/sh /etc/rc.shutdown".
<.> `exec.consolelog` — файл для вывода результатов выполнения команды (stdout и stderr).
<.> `allow.raw_sockets` — разрешает создание raw-сокетов внутри клетки. Установка этого параметра позволяет использовать такие утилиты, как man:ping[8] и man:traceroute[8], внутри клетки.
<.> `exec.clean` — выполнение команд в чистом окружении.
<.> `mount.devfs` - монтирование файловой системы man:devfs[5] в каталог [.filename]#/dev# внутри chroot, чтобы ограничить видимые в клетке устройства. Применяется набор правил, заданный параметром `devfs_ruleset`, или, когда этот параметр оставлен по умолчанию равным `0`, используется запасной вариант `mount.devfs` с набором правил 4, `devfsrules_jail`, который скрывает все устройства хоста, кроме минимального набора, безопасного для клетки (см. crossref:jails[jail-conf-devfs, Devfs Rulesets]).
<.> `host.hostname` — имя хоста для клетки.
<.> `path` — каталог, который будет корневым для клетки. Любые команды, выполняемые внутри клетки, либо с помощью `jail`, либо через man:jexec[8], запускаются из этого каталога.
<.> `ip4.addr` - Один IPv4-адрес или разделённый запятыми список адресов, назначенных клетке. Установка `ip4.addr` подразумевает `ip4 = new`; сам параметр `ip4` не имеет значения по умолчанию (если он не указан, то его значение зависит от значений других параметров). Параметр `ip4` принимает три значения: `new` ограничивает клетку адресами, перечисленными в `ip4.addr`, `inherit` предоставляет клетке неограниченный доступ ко всем IPv4-адресам хоста, а `disable` полностью отключает IPv4 в клетке. Клетка, в которой не заданы ни `ip4`, ни `ip4.addr`, ведёт себя как при `inherit`, и параметры `interface` и `ip4.addr` не действуют при `inherit`. В клетке с несколькими адресами `ip4.saddrsel` позволяет ядру выбирать исходный адрес для исходящих соединений на несвязанных сокетах, тогда как `ip4.nosaddrsel` принудительно использует первый адрес из списка.
<.> `ip6.addr` - Один IPv6-адрес или разделенный запятыми список адресов, назначенных клетке. Параметр `ip6` принимает те же значения `new`, `inherit` и `disable`, что и `ip4`, при этом `ip6.saddrsel` и `ip6.nosaddrsel` управляют выбором адреса источника. Используйте `ip6 = disable` для клетки только с IPv4 или `ip4 = disable` для клетки только с IPv6.
<.> `interface` — Сетевой интерфейс для добавления IP-адресов клетки. Обычно это интерфейс хоста.

[NOTE]
====
Клетка не имеет виртуальной консоли: man:init[8] и man:getty[8] не запускаются внутри неё, поэтому не к чему подключиться в интерактивном режиме. Файл `exec.consolelog` захватывает только стандартный вывод и стандартный поток ошибок команд `exec.*`, то есть вывод сценариев [.filename]#/etc/rc# запуска и [.filename]#/etc/rc.shutdown# завершения работы. Это не интерактивная консоль; для этого используйте man:jexec[8] (см. crossref:jails[access-jail, Доступ к клетке]) или запустите man:sshd[8] внутри клетки. Собственные демоны клетки отправляют свой последующий вывод в syslog клетки.
====

[TIP]
====
Файлы `exec.consolelog` растут без ограничений. Выполняйте их ротацию с помощью man:newsyslog[8], добавив одну запись с подстановочным знаком (glob), например, в [.filename]#/etc/newsyslog.conf.d/jail#:

[.programlisting]
....
/var/log/jail_console_*.log            640  7     100  *     J
....
====

[[global-settings-wildcards-variables]]
==== Глобальные параметры, подстановочные знаки и переменные

Параметры, заданные вне любого блока клетки, являются подстановочными значениями по умолчанию: man:jail.conf[5] применяет их к каждой клетке, точно так же, как если бы они были записаны в разделе подстановочного знака `*`. Установка общих параметров один раз делает каждый блок клетки коротким.

Имя, перед которым стоит знак доллара и которое, при необходимости, заключено в фигурные скобки, заменяется значением этого параметра или переменной. Встроенное выражение `${name}` разворачивается в собственное имя клетки, а пользовательская переменная определяется точно так же, как параметр, но с ведущим знаком доллара.

Следующий [.filename]#/etc/jail.conf# определяет общий заголовок один раз и переопределяет только то, что отличается для каждой клетки:

[.programlisting]
....
# Shared defaults applied to every jail
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;
host.hostname = "${name}";
path = "/usr/local/jails/containers/${name}";
interface = em0;

www {
    ip4.addr = 192.168.1.151;
}

db {
    host.hostname = "database.example.com";
    ip4.addr = 192.168.1.152;
}
....

Здесь `www` наследует всё из заголовка и задаёт только свой адрес, тогда как `db` также переопределяет `host.hostname`.

[[jail-conf-hooks]]
==== Командные хуки

man:jail[8] запускает клетку через последовательность перехватчиков жизненного цикла (хуков), и важно, выполняется ли каждый из хуков в среде хост-системы или внутри клетки. В порядке создания:

`exec.prestart`:: Выполняется в среде хост-системы до создания клетки.
`exec.created`:: Выполняется в среде хост-системы после создания клетки, но до запуска любого процесса внутри неё.
`exec.start`:: Выполняется внутри клетки, обычно `/bin/sh /etc/rc` для запуска её служб.
`exec.poststart`:: Выполняется в среде хост-системы после запуска клетки.

При завершении работы хуки выполняется зеркально в обратном порядке:

`exec.prestop`:: Выполняется в среде хост-системы до остановки клетки.
`exec.stop`:: Выполняется внутри клетки, обычно `/bin/sh /etc/rc.shutdown`.
`exec.poststop`:: Выполняется в среде хост-системы после остановки клетки, чтобы разобрать всё, что было настроено с помощью `exec.prestart`.

Хуку может быть назначено более одной команды путём добавления с помощью `+=`:

[.programlisting]
....
exec.poststart += "logger jail ${name} started";
....

`exec.timeout` ограничивает время, в течение которого man:jail[8] ожидает завершения команды, а `exec.consolelog` захватывает вывод этих команд. В разделе VNET (crossref:jails[creating-vnet-jail, VNET Jails]) используются `exec.prestart` и `exec.poststop` для настройки и демонтажа сетевой инфраструктуры каждой клетки на хосте.

Тогда как хуки упорядочивают команды внутри одной клетки, параметр `depend` упорядочивает запуск целых клеток. Запись `depend = otherjail;` сообщает man:jail[8], что эта клетка требует `otherjail`: зависимость создаётся первой, до завершения её последней команды `exec.poststart`, прежде чем будет создана эта клетка, и клетки уничтожаются в обратном порядке.

[[jail-conf-devfs]]
==== Наборы правил Devfs

Каждая клетка, которая устанавливает `mount.devfs`, получает набор правил devfs, определяющий, какие узлы устройств появляются в её [.filename]#/dev#: либо тот, который задан параметром `devfs_ruleset`, либо, когда этот параметр не установлен, значение по умолчанию для `mount.devfs` — набор правил 4. man:devfs.rules[5] определяет именованные наборы правил в [.filename]#/etc/defaults/devfs.rules#. Два из них предназначены для клеток: `devfsrules_jail`, набор правил 4, является стандартным минимальным набором устройств для клетки, а `devfsrules_jail_vnet`, набор правил 5, добавляет [.filename]#/dev/pf#, чтобы клетка VNET могла запускать собственный межсетевой экран. Примеры клеток VNET устанавливают `devfs_ruleset = 5`, а пример клетки Linux устанавливает `devfs_ruleset = 4`; примеры классической и тонкой клеток устанавливают только `mount.devfs` и поэтому используют его значение по умолчанию — набор правил 4.

Чтобы открыть доступ к дополнительному устройству, напишите пользовательский набор правил в [.filename]#/etc/devfs.rules#, который включает один из стандартных наборов правил и показывает то, что ему необходимо. Следующий набор правил начинается с `devfsrules_jail` и дополнительно показывает устройства man:bpf[4]:

[.programlisting]
....
[devfsrules_jail_bpf=100]
add include $devfsrules_jail
add path 'bpf*' unhide
....

Перезагрузите правила, затем укажите новый набор правил по его номеру в конфигурации клетки:

[source, shell]
....
# service devfs restart
....

[.programlisting]
....
devfs_ruleset = 100;
....

Правила в [.filename]#/etc/devfs.rules# переопределяют правила с теми же номерами в [.filename]#/etc/defaults/devfs.rules#; в остальных случаях эффект двух файлов фактически объединяется.

Дополнительную информацию о переменных конфигурации можно найти в man:jail[8] и man:jail.conf[5].

[[classic-jail]]
== Классическая клетка (Толстая клетка)

Эти клетки напоминают настоящую систему FreeBSD. Ими можно управлять почти так же, как обычной хост-системой, и обновлять независимо.

[[creating-classic-jail]]
=== Создание классической клетки

В принципе, для клетки требуется только имя хоста, корневой каталог, IP-адрес и пользовательское пространство.

Пользовательское окружение для клетки можно получить с официальных серверов загрузки FreeBSD.

Выполните следующую команду, чтобы загрузить пользовательское окружение:

[source, shell, subs=attributes]
....
# fetch https://download.freebsd.org/releases/amd64/amd64/{rel-latest}-RELEASE/base.txz -o /usr/local/jails/media/{rel-latest}-RELEASE-base.txz
....

После завершения загрузки необходимо извлечь содержимое в каталог клетки.

Выполните следующие команды, чтобы извлечь пользовательское окружение в каталог клетки:

[source, shell, subs=attributes]
....
# mkdir -p /usr/local/jails/containers/classic
# tar -xf /usr/local/jails/media/{rel-latest}-RELEASE-base.txz -C /usr/local/jails/containers/classic --unlink
....

С извлечённой пользовательской средой в каталоге клетки потребуется скопировать файлы часового пояса и DNS-сервера:

[source, shell]
....
# cp /etc/resolv.conf /usr/local/jails/containers/classic/etc/resolv.conf
# cp /etc/localtime /usr/local/jails/containers/classic/etc/localtime
....

Скопировав файлы, следующим шагом будет обновление до последнего уровня исправлений с помощью выполнения следующей команды:

[source, shell]
....
# freebsd-update -b /usr/local/jails/containers/classic/ fetch install
....

Последним шагом является настройка клетки. Необходимо добавить запись в конфигурационный файл [.filename]#/etc/jail.conf# или в [.filename]#jail.conf.d# с параметрами клетки.

Пример может выглядеть следующим образом:

[.programlisting]
....
classic {
  # STARTUP/LOGGING
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # PERMISSIONS
  allow.raw_sockets;
  exec.clean;
  mount.devfs;

  # HOSTNAME/PATH
  host.hostname = "${name}";
  path = "/usr/local/jails/containers/${name}";

  # NETWORK
  ip4.addr = 192.168.1.151;
  interface = em0;
}
....

Выполните следующую команду для запуска клетки:

[source, shell]
....
# service jail start classic
....

Дополнительная информация об управлении клетками приведена в разделе crossref:jails[jail-management,Управление клетками].

[[creating-jail-userland]]
=== Получение пользовательского окружения клетки

Процедура fetch-and-extract, показанная выше, является традиционным способом заполнения корневого каталога клетки, но это не единственный способ. Та же самая пользовательская среда может быть установлена с помощью man:bsdinstall[8], из базовых системных пакетов или из локально собранного дерева исходных кодов.

man:bsdinstall[8] предоставляет выделенную цель `jail`, которая настраивает новое пользовательское окружение в заданном каталоге для использования с man:jail[8]. Она загружает и распаковывает наборы дистрибутивов без установки ядра, пропуская шаги разбиения диска и настройки сети обычной установки, что делает её однокомандным скриптом — альтернативой ручной последовательности загрузки и man:tar[1] следующим образом:

[source, shell]
....
# bsdinstall jail /usr/local/jails/containers/classic
....

Цель `jail` по умолчанию устанавливает базовый дистрибутив ([.filename]#base.txz#). Начиная с FreeBSD 15.0 и более поздних версий, man:bsdinstall[8] также может заполнять корневой каталог пакетами базовой системы с помощью своей цели `pkgbase`; метод на основе пакетов описан далее.

Начиная с FreeBSD 15.0, базовая система также публикуется в виде пакетов, поэтому пользовательское окружение клетки можно установить полностью с помощью man:pkg[8] из репозитория `FreeBSD-base`. Укажите man:pkg[8] на корневой каталог клетки с помощью глобальной опции `--rootdir` и установите вариант клетки для базового набора:

[source, shell]
....
# pkg --rootdir /usr/local/jails/containers/classic install -r FreeBSD-base FreeBSD-set-base-jail
....

Используйте `FreeBSD-set-minimal-jail` вместо `FreeBSD-set-base-jail` для минимального пользовательского окружения. Клетка, установленная таким образом, впоследствии обновляется и получает исправления с помощью man:pkg[8], а не man:freebsd-update[8]; см. crossref:jails[jail-upgrade-pkgbase, Обновление клеток pkgbase].

[NOTE]
====
Пакеты базовой системы — это технологическая предварительная версия в FreeBSD 15.0. Репозиторий `FreeBSD-base` определён в [.filename]#/etc/pkg/FreeBSD.conf#, но по умолчанию отключён, поэтому выбирайте его явно с помощью `install -r FreeBSD-base`. Целевой корневой каталог и его подкаталоги [.filename]#var/cache/pkg# и [.filename]#var/db/pkg# возможно, потребуется создать заранее. Традиционное распространение [.filename]#base.txz# по-прежнему публикуется для FreeBSD 15.0, поэтому метод fetch-and-extract продолжает работать и там.
====

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

[source, shell]
....
# cd /usr/src
# make buildworld
# make installworld DESTDIR=/usr/local/jails/containers/classic
# make distribution DESTDIR=/usr/local/jails/containers/classic
....

`make distribution` устанавливает файлы конфигурации по умолчанию в [.filename]#/etc# клетки и выполняется только один раз, при заполнении клетки; повторение этого действия во время обновления перезапишет конфигурацию внутри клетки. Чтобы обновить такую клетку из исходников, следуйте той же последовательности, что и для хост-системы: запустите `installworld` с тем же `DESTDIR`, объедините файлы конфигурации с помощью `etcupdate -D`, указав корневой каталог клетки, и удалите устаревшие файлы и библиотеки с помощью `make delete-old delete-old-libs`, снова с тем же `DESTDIR`, вместо использования man:freebsd-update[8]. Полную процедуру обновления из исходного кода см. в crossref:cutting-edge[makeworld, Обновление FreeBSD из исходного кода].

[[classic-jail-clone]]
=== Клонирование клетки как шаблона

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

В OpenZFS это делается путем создания снимка настроенного набора данных клетки и его клонирования. Это требует, чтобы исходная клетка располагалась в собственном наборе данных, поэтому создайте её с помощью `zfs create`, а не `mkdir`, как в классической процедуре клетки выше:

[source, shell]
....
# zfs create -p zroot/jails/containers/classic
....

Остановите клетку перед созданием снимка, чтобы образ был консистентным, затем создайте снимок и клонируйте набор данных один раз для каждой новой клетки:

[source, shell]
....
# service jail stop classic
# zfs snapshot zroot/jails/containers/classic@template
# zfs clone zroot/jails/containers/classic@template zroot/jails/containers/web1
....

На хосте UFS скопируйте дерево каталогов настроенной клетки вместо этого:

[source, shell]
....
# cp -a /usr/local/jails/containers/classic /usr/local/jails/containers/web1
....

Каждый клон требует своей собственной записи в [.filename]#/etc/jail.conf#, или в [.filename]#jail.conf.d#, с уникальным именем, путем и IP-адресом. Поскольку параметры `path` и `host.hostname` в приведенном выше примере выводятся из имени блока с помощью `${name}`, отличаются только имя блока и адрес:

[.programlisting]
....
web1 {
  # STARTUP/LOGGING
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # PERMISSIONS
  allow.raw_sockets;
  exec.clean;
  mount.devfs;

  # HOSTNAME/PATH
  host.hostname = "${name}";
  path = "/usr/local/jails/containers/${name}";

  # NETWORK
  ip4.addr = 192.168.1.152;
  interface = em0;
}
....

Клон шаблонного образа отличается от тонкой клетки (crossref:jails[thin-jail, Тонкие клетки]). Тонкая клетка продолжает использовать общую базу только для чтения и обслуживается один раз в шаблоне, тогда как каждый клон шаблонного образа представляет собой полную, независимо записываемую копию, которая обновляется и модернизируется отдельно, как описано в crossref:jails[jail-upgrading, Обновление клетки]. Клон OpenZFS изначально является эффективной по пространству копией при записи (copy-on-write), которая разделяет блоки со снимком, и сохраняет зависимость от этого исходного снимка, пока `zfs promote` не разорвёт её. Для полностью независимой копии или для репликации клетки на другой пул или хост используйте `zfs send`, переданный через конвейер в `zfs receive`, вместо `zfs clone`.

[[thin-jail]]
== Тонкие клетки (Thin Jails)

Хотя тонкие клетки используют ту же технологию, что и толстые клетки, процедура их создания отличается. Тонкие клетки можно создавать с использованием снимков OpenZFS или шаблонов и NullFS. Использование снимков OpenZFS и шаблонов с NullFS имеет определённые преимущества перед классическими клетками, например, возможность быстрого создания из снимков или обновления нескольких клеток с помощью NullFS.

[[creating-thin-jail-openzfs-snapshots]]
=== Создание тонкой клетки с использованием снимков OpenZFS

Благодаря хорошей интеграции между FreeBSD и OpenZFS создание новых тонких клеток с использованием снимков OpenZFS очень просто.

Для создания тонкой клетки с использованием снимков OpenZFS первым шагом является создание дерева каталогов клетки, следуя инструкциям в разделе crossref:jails[host-configuration-directories, "Настройка дерева каталогов клетки"].

Далее создайте шаблон. Шаблоны будут использоваться только для создания новых клеток. По этой причине они создаются в режиме "только для чтения", чтобы клетки создавались на неизменяемой основе.

Для создания набора данных для шаблона выполните следующую команду:

[source, shell, subs=attributes]
....
# zfs create -p zroot/jails/templates/{rel-latest}-RELEASE
....

Затем выполните следующую команду для загрузки пользовательской среды:

[source, shell, subs=attributes]
....
# fetch https://download.freebsd.org/releases/amd64/amd64/{rel-latest}-RELEASE/base.txz -o /usr/local/jails/media/{rel-latest}-RELEASE-base.txz
....

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

[source, shell, subs=attributes]
....
# tar -xf /usr/local/jails/media/{rel-latest}-RELEASE-base.txz -C /usr/local/jails/templates/{rel-latest}-RELEASE --unlink
....

Когда пользовательская среда записана в каталог шаблонов, необходимо скопировать файлы часового пояса и DNS-сервера в каталог шаблона, выполнив следующую команду:

[source, shell, subs=attributes]
....
# cp /etc/resolv.conf /usr/local/jails/templates/{rel-latest}-RELEASE/etc/resolv.conf
# cp /etc/localtime /usr/local/jails/templates/{rel-latest}-RELEASE/etc/localtime
....

Следующее, что нужно сделать, — обновиться до последнего уровня исправлений, выполнив следующую команду:

[source, shell, subs=attributes]
....
# freebsd-update -b /usr/local/jails/templates/{rel-latest}-RELEASE/ fetch install
....

После завершения обновления шаблон готов.

Для создания снимка OpenZFS из шаблона выполните следующую команду:

[source, shell, subs=attributes]
....
# zfs snapshot zroot/jails/templates/{rel-latest}-RELEASE@base
....

После создания снимка OpenZFS можно создавать бесконечное количество клеток с помощью функции клонирования OpenZFS.

Для создания тонкой клетки с именем `thinjail` выполните следующую команду:

[source, shell, subs=attributes]
....
# zfs clone zroot/jails/templates/{rel-latest}-RELEASE@base zroot/jails/containers/thinjail
....

Последним шагом является настройка клетки. Необходимо добавить запись в конфигурационный файл [.filename]#/etc/jail.conf# или в [.filename]#jail.conf.d# с параметрами клетки.

Пример может выглядеть следующим образом:

[.programlisting]
....
thinjail {
  # STARTUP/LOGGING
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # PERMISSIONS
  allow.raw_sockets;
  exec.clean;
  mount.devfs;

  # HOSTNAME/PATH
  host.hostname = "${name}";
  path = "/usr/local/jails/containers/${name}";

  # NETWORK
  ip4 = inherit;
  interface = em0;
}
....

Выполните следующую команду для запуска клетки:

[source, shell]
....
# service jail start thinjail
....

Дополнительная информация об управлении клетками приведена в разделе crossref:jails[jail-management,Управление клетками].

[[creating-thin-jail-nullfs]]
=== Создание тонкой клетки с использованием NullFS

Клетка может быть создана с уменьшенным дублированием системных файлов с использованием техники тонкой клетки и NullFS для выборочного совместного использования определённых каталогов из хост-системы в клетке.

Первым шагом является создание набора данных для сохранения шаблона, выполните следующую команду, если используется OpenZFS:

[source, shell, subs=attributes]
....
# zfs create -p zroot/jails/templates/{rel-latest}-RELEASE-base
....

Или эту, если используется UFS:

[source, shell, subs=attributes]
....
# mkdir /usr/local/jails/templates/{rel-latest}-RELEASE-base
....

Затем выполните следующую команду для загрузки пользовательской среды:

[source, shell, subs=attributes]
....
# fetch https://download.freebsd.org/releases/amd64/amd64/{rel-latest}-RELEASE/base.txz -o /usr/local/jails/media/{rel-latest}-RELEASE-base.txz
....

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

[source, shell, subs=attributes]
....
# tar -xf /usr/local/jails/media/{rel-latest}-RELEASE-base.txz -C /usr/local/jails/templates/{rel-latest}-RELEASE-base --unlink
....

Когда пользовательская среда записана в каталог шаблонов, необходимо скопировать файлы часового пояса и DNS-сервера в каталог шаблона, выполнив следующую команду:

[source, shell, subs=attributes]
....
# cp /etc/resolv.conf /usr/local/jails/templates/{rel-latest}-RELEASE-base/etc/resolv.conf
# cp /etc/localtime /usr/local/jails/templates/{rel-latest}-RELEASE-base/etc/localtime
....

После перемещения файлов в шаблон следующим шагом будет обновление до последнего уровня исправлений с помощью выполнения следующей команды:

[source, shell, subs=attributes]
....
# freebsd-update -b /usr/local/jails/templates/{rel-latest}-RELEASE-base/ fetch install
....

В дополнение к базовому шаблону также необходимо создать каталог, где будет располагаться `skeleton`. Некоторые каталоги будут скопированы из шаблона в `skeleton`.

Выполните следующую команду, чтобы создать набор данных для `skeleton` в случае использования OpenZFS:

[source, shell, subs=attributes]
....
# zfs create -p zroot/jails/templates/{rel-latest}-RELEASE-skeleton
....

Или эту в случае использования UFS:

[source, shell, subs=attributes]
....
# mkdir /usr/local/jails/templates/{rel-latest}-RELEASE-skeleton
....

Затем создайте `skeleton`-каталоги. `Skeleton`-каталоги будут содержать локальные каталоги для клеток.

Выполните следующие команды для создания каталогов:

[source, shell, subs=attributes]
....
# mkdir -p /usr/local/jails/templates/{rel-latest}-RELEASE-skeleton/home
# mkdir -p /usr/local/jails/templates/{rel-latest}-RELEASE-skeleton/usr
# mv /usr/local/jails/templates/{rel-latest}-RELEASE-base/etc /usr/local/jails/templates/{rel-latest}-RELEASE-skeleton/etc
# mv /usr/local/jails/templates/{rel-latest}-RELEASE-base/usr/local /usr/local/jails/templates/{rel-latest}-RELEASE-skeleton/usr/local
# mv /usr/local/jails/templates/{rel-latest}-RELEASE-base/tmp /usr/local/jails/templates/{rel-latest}-RELEASE-skeleton/tmp
# mv /usr/local/jails/templates/{rel-latest}-RELEASE-base/var /usr/local/jails/templates/{rel-latest}-RELEASE-skeleton/var
# mv /usr/local/jails/templates/{rel-latest}-RELEASE-base/root /usr/local/jails/templates/{rel-latest}-RELEASE-skeleton/root
....

Следующий шаг — создать символьные ссылки на `skeleton`, выполнив следующие команды:

[source, shell, subs=attributes]
....
# cd /usr/local/jails/templates/{rel-latest}-RELEASE-base/
# mkdir skeleton
# ln -s skeleton/etc etc
# ln -s skeleton/home home
# ln -s skeleton/root root
# ln -s ../skeleton/usr/local usr/local
# ln -s skeleton/tmp tmp
# ln -s skeleton/var var
....

Когда `skeleton` готов, необходимо скопировать данные в клетку.

В случае использования OpenZFS, снимки OpenZFS могут быть использованы для простого создания необходимого количества клеток с помощью выполнения следующих команд:

[source, shell, subs=attributes]
....
# zfs snapshot zroot/jails/templates/{rel-latest}-RELEASE-skeleton@base
# zfs clone zroot/jails/templates/{rel-latest}-RELEASE-skeleton@base zroot/jails/containers/thinjail
....

В случае использования UFS можно использовать программу man:cp[1], выполнив следующую команду:

[source, shell, subs=attributes]
....
# cp -R /usr/local/jails/templates/{rel-latest}-RELEASE-skeleton /usr/local/jails/containers/thinjail
....

Затем создайте каталог, в котором будут смонтированы базовый шаблон и skeleton:

[source, shell]
....
# mkdir -p /usr/local/jails/thinjail-nullfs-base
....

Добавьте запись о клетке в [.filename]#/etc/jail.conf# или файл в [.filename]#jail.conf.d# следующим образом:

[.programlisting]
....
thinjail {
  # STARTUP/LOGGING
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # PERMISSIONS
  allow.raw_sockets;
  exec.clean;
  mount.devfs;

  # HOSTNAME/PATH
  host.hostname = "${name}";
  path = "/usr/local/jails/${name}-nullfs-base";

  # NETWORK
  ip4.addr = 192.168.1.153;
  interface = em0;

  # MOUNT
  mount.fstab = "/usr/local/jails/${name}-nullfs-base.fstab";
}
....

Затем создайте файл [.filename]#/usr/local/jails/thinjail-nullfs-base.fstab# следующего содержания:

[.programlisting, subs=attributes]
....
/usr/local/jails/templates/{rel-latest}-RELEASE-base  /usr/local/jails/thinjail-nullfs-base/ nullfs   ro          0 0
/usr/local/jails/containers/thinjail     /usr/local/jails/thinjail-nullfs-base/skeleton nullfs  rw  0 0
....

Файл [.filename]#fstab# управляет тем, как man:mount_nullfs[8] представляет базовый шаблон и скелет внутри клетки. Базовый шаблон монтируется только для чтения (`ro`), чтобы каждая клетка NullFS использовала одну неизменяемую копию базовой системы. Скелет каждой клетки монтируется с возможностью чтения и записи (`rw`), и именно здесь каждая клетка хранит свои собственные доступные для записи каталоги [.filename]#/etc#, [.filename]#/var#, [.filename]#/usr/local# и другие локальные каталоги. Поскольку все клетки используют один и тот же базовый шаблон только для чтения, обновление одного шаблона сразу обновляет все клетки NullFS, в отличие от независимых клонов OpenZFS, каждый из которых необходимо обновлять отдельно.

Выполните следующую команду для запуска клетки:

[source, shell]
....
# service jail start thinjail
....

[[creating-vnet-jail]]
== Клетки VNET

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

Первым шагом для создания VNET-клетки является создание man:bridge[4] с помощью выполнения следующей команды:

[source, shell]
....
# ifconfig bridge create
....

Вывод должен быть похож на следующий:

[.programlisting]
....
bridge0
....

[WARNING]
====
Назначение IP-адреса интерфейсу, который является членом моста, устарело. Назначайте адрес хоста самому `bridge0`, а не `em0`, точно так же, как это делает приведённый ниже блок [.filename]#/etc/rc.conf#. Такие конфигурации пока продолжают работать: FreeBSD 15.0 записывает предупреждение ядра, когда интерфейс с адресом добавляется в мост, и отказывает ему с `EINVAL` только тогда, когда sysctl `net.link.bridge.member_ifaddrs` изменён с его значения по умолчанию `1` на `0`. man:bridge[4] объявляет об удалении этого sysctl в FreeBSD 16.0, после чего адреса на членах моста больше не будут разрешены.
====

Создав `bridge`, необходимо подключить его к интерфейсу `em0` и включить оба, выполнив следующие команды:

[source, shell]
....
# ifconfig bridge0 addm em0 up
# ifconfig em0 up
....

Чтобы сохранить этот параметр после перезагрузки, добавьте следующие строки в [.filename]#/etc/rc.conf#:

[.programlisting]
....
defaultrouter="192.168.1.1"
cloned_interfaces="bridge0"
ifconfig_bridge0="inet 192.168.1.150/24 addm em0 up"
ifconfig_em0="up"
....

Для получения дополнительной информации о мостах см. crossref:advanced-networking[network-bridging, Сетевые мосты].

Следующий шаг — создать клетку, как указано выше.

Можно использовать как процедуру crossref:jails[classic-jail, Классической Клетки (Толстой клетки)], так и процедуру crossref:jails[thin-jail, Тонких Клеток]. Единственное, что изменится — это конфигурация в файле [.filename]#/etc/jail.conf#.

В качестве примера для созданной клетки будет использоваться путь [.filename]#/usr/local/jails/containers/vnet#.

Вот пример конфигурации для VNET-клетки:

[.programlisting]
....
vnet {
  # STARTUP/LOGGING
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # PERMISSIONS
  allow.raw_sockets;
  exec.clean;
  mount.devfs;
  devfs_ruleset = 5;

  # PATH/HOSTNAME
  path = "/usr/local/jails/containers/${name}";
  host.hostname = "${name}";

  # VNET/VIMAGE
  vnet;
  vnet.interface = "${epair}b";

  # NETWORKS/INTERFACES
  $id = "154"; <.>
  $ip = "192.168.1.${id}/24";
  $gateway = "192.168.1.1";
  $bridge = "bridge0"; <.>
  $epair = "epair${id}";

  # ADD TO bridge INTERFACE
  exec.prestart  = "/sbin/ifconfig ${epair} create up";
  exec.prestart += "/sbin/ifconfig ${epair}a up descr jail:${name}";
  exec.prestart += "/sbin/ifconfig ${bridge} addm ${epair}a up";
  exec.start    += "/sbin/ifconfig ${epair}b ${ip} up";
  exec.start    += "/sbin/route add default ${gateway}";
  exec.start    += "/bin/sh /etc/rc";
  exec.stop      = "/bin/sh /etc/rc.shutdown";
  exec.poststop = "/sbin/ifconfig ${bridge} deletem ${epair}a";
  exec.poststop += "/sbin/ifconfig ${epair}a destroy";
}
....

<.> Номер, *уникальный* для каждой клетки, используемый для получения как имени устройства epair (`epair${id}`), так и последнего октета адреса клетки (`192.168.1.${id}`); присвойте каждой клетке на мосту различное значение.
<.> Относится к ранее созданному мосту.

Параметр `vnet.interface` задает имя интерфейса, который man:jail[8] перемещает в сетевой стек клетки после ее создания, здесь это сторона epair, относящаяся к клетке, `${epair}b`. Этот интерфейс исчезает с хоста, пока клетка работает, и автоматически возвращается хосту при остановке клетки. Физическая сетевая карта или виртуальная функция SR-IOV могут быть переданы клетке таким же образом, обеспечивая ей выделенную сеть без моста или epair.

Строка `devfs_ruleset = 5` применяет набор правил `devfsrules_jail_vnet` из [.filename]#/etc/defaults/devfs.rules#, который расширяет стандартный набор правил для клеток, открывая доступ к [.filename]#/dev/pf#. Таким образом, клетка VNET может запускать собственный межсетевой экран man:pf[4] с `pf_enable="YES"` и приватным [.filename]#pf.conf#, независимым от хоста. Смотрите crossref:jails[jail-conf-devfs, Devfs Rulesets] для описания определений наборов правил и crossref:firewalls[firewalls, Firewalls] для информации о самом pf.

[[jail-vnet-jib]]
=== Автоматизация сети VNET с помощью jib

Настройка epair и подключение его к мосту вручную в `exec.prestart` и `exec.poststop`, как показано выше, становится утомительной при большом количестве клеток. В базовой системе есть вспомогательный скрипт, [.filename]#/usr/share/examples/jails/jib#, который выполняет ту же самую настройку автоматически.

Ссылайтесь на него из хуков клетки вместо пяти самодельных строк man:ifconfig[8]:

[.programlisting]
....
  exec.prestart += "jib addm ${name} em0";
  exec.poststop += "jib destroy ${name}";

  vnet.interface = "e0b_${name}";
....

`jib addm` создает epair, чья сторона хоста называется `e0a_<name>`, а сторона клетки — `e0b_<name>`, затем присоединяет сторону хоста к мосту, который он создает автоматически, названному в честь интерфейса-члена (для `em0` мост называется `em0bridge`). Укажите эту сторону клетки в `vnet.interface`. Когда клетка останавливается, `jib destroy` удаляет epair. Скрипт также предоставляет подкоманду `jib show` для вывода списка интерфейсов, которыми он управляет.

[[jail-vnet-jng]]
=== Сетевые взаимодействия Netgraph VNET с помощью jng

Третий режим работы сети, упомянутый в обзоре crossref:jails[jails-networking, Сеть], использует man:netgraph[4] вместо man:bridge[4] и epair. В базовой системе поставляется второй вспомогательный скрипт, [.filename]#/usr/share/examples/jails/jng#, который создает `ng_bridge`, подключенный к `ng_eiface` для каждой клетки:

[.programlisting]
....
  exec.prestart += "jng bridge ${name} em0";
  exec.poststop += "jng shutdown ${name}";

  vnet.interface = "ng0_${name}";
....

`jng bridge` создает узлы netgraph и интерфейс на стороне клетки с именем `ng0_<name>`, который указывается в `vnet.interface`; `jng shutdown` разрушает топологию при остановке клетки. Результат функционально эквивалентен настройке с epair и bridge, но построен на узлах netgraph, а не на man:bridge[4].

[[jail-vnet-firewall]]
=== Запуск межсетевого экрана и DHCP внутри клетки VNET

Поскольку `devfs_ruleset = 5` открывает доступ к [.filename]#/dev/pf#, клетка VNET может запускать собственный межсетевой экран. Включите его внутри клетки точно так же, как на любом хосте, с помощью `pf_enable="YES"` в [.filename]#/etc/rc.conf# клетки и её собственном [.filename]#/etc/pf.conf#.

Однако правила набора 5 не открывают доступ к [.filename]#/dev/bpf#. Клетка VNET, которая получает свой адрес через DHCP, например, с помощью `ifconfig_e0b_myjail="SYNCDHCP"` в её [.filename]#/etc/rc.conf#, запускает man:dhclient[8], которому требуется bpf, и поэтому он терпит неудачу при использовании набора правил 5. Следуя рецепту из crossref:jails[jail-conf-devfs,Devfs Rulesets], определите собственный набор правил в [.filename]#/etc/devfs.rules#, который начинается с `devfsrules_jail_vnet` и дополнительно открывает устройства bpf:

[.programlisting]
....
[devfsrules_jail_vnet_dhcp=101]
add include $devfsrules_jail_vnet
add path 'bpf*' unhide
....

После выполнения `service devfs restart` на хосте, укажите клетке на новый набор правил с помощью `devfs_ruleset = 101;`.

[[creating-linux-jail]]
== Клетки Linux

FreeBSD может запускать пользовательские программы Linux внутри клетки, используя crossref:linuxemu[linuxemu, Двоичную совместимость с Linux] и package:sysutils/debootstrap[]. Клетки не имеют собственного ядра. Они работают на ядре хоста. Поэтому необходимо включить Двоичную совместимость с Linux в хост-системе.

Чтобы включить ABI Linux при загрузке, выполните следующую команду:

[source, shell]
....
# sysrc linux_enable="YES"
....

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

[source, shell]
....
# service linux start
....

Следующим шагом будет создание клетки, как указано выше, например, в разделе crossref:jails[creating-thin-jail-openzfs-snapshots, Создание тонкой клетки с использованием снимков OpenZFS], но *без* выполнения настройки. Клетки FreeBSD Linux требуют особой конфигурации, которая будет подробно описана ниже.

Если на хосте ещё нет файла [.filename]#/etc/jail.conf# — например, в системе, где клетки до сих пор создавались только из командной строки, — создайте пустой файл, чтобы man:service[8] затем мог остановить клетку:

[source, shell]
....
# touch /etc/jail.conf
....

Скрипт man:service[8] jail читает [.filename]#/etc/jail.conf# при остановке клетки, поэтому команда остановки завершается ошибкой, если файл не существует. Если [.filename]#/etc/jail.conf# уже присутствует из предыдущего раздела, этот шаг не требуется.

Следующая команда man:jail[8] выполняет необходимую настройку для клетки и запускает её напрямую, без записи в [.filename]#/etc/jail.conf#. Флаг `-c` создаёт новую клетку, а `-m` изменяет существующую; в сочетании как `-cm`, man:jail[8] создаёт клетку, если она ещё не существует, и обновляет её, если она существует:

[source, shell]
....
# jail -cm \
    name=ubuntu \
    host.hostname="ubuntu.example.com" \
    path="/usr/local/jails/containers/ubuntu" \
    interface="em0" \
    ip4.addr="192.168.1.155" \
    exec.start="/bin/sh /etc/rc" \
    exec.stop="/bin/sh /etc/rc.shutdown" \
    mount.devfs \
    devfs_ruleset=4 \
    allow.mount \
    allow.mount.devfs \
    allow.mount.fdescfs \
    allow.mount.procfs \
    allow.mount.linprocfs \
    allow.mount.linsysfs \
    allow.mount.tmpfs \
    enforce_statfs=1
....

Получите доступ к вновь созданной клетке как `root` с помощью man:jexec[8]:

[source, shell]
....
# jexec -u root ubuntu sh
....

Внутри клетки установите package:sysutils/debootstrap[] и используйте его для сборки пользовательского окружения Ubuntu в [.filename]#/compat/ubuntu#. package:sysutils/debootstrap[] — это инструмент, который собирает пользовательское окружение Linux; после этого он не требуется для входа в клетку. Первый запуск man:pkg[7] автоматически выполняет его начальную настройку:

[source, shell]
....
# pkg install debootstrap
# debootstrap jammy /compat/ubuntu
....

`jammy` выбирает Ubuntu 22.04 LTS. Можно выбрать более новую LTS, например `noble` (Ubuntu 24.04), при условии, что она работает в linuxulator.

Когда процесс завершится и на консоли появится сообщение `Base system installed successfully`, из хост-системы остановите клетку. Так как эта клетка не активирована в [.filename]#/etc/rc.conf# и еще не перечислена в [.filename]#/etc/jail.conf#, используйте действие man:service[8] с префиксом `one`, которое запускает клетки, которые не активированы:

[source, shell]
....
# service jail onestop ubuntu
....

Действия `onestart` и `onestop` являются аналогами `start` и `stop` для клеток, которые не включены через `jail_enable` или не перечислены в `jail_list`.

Затем добавьте запись в [.filename]#/etc/jail.conf# для Linux-контейнера:

[.programlisting]
....
ubuntu {
  # STARTUP/LOGGING
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # PERMISSIONS
  allow.raw_sockets;
  exec.clean;
  mount.devfs;
  devfs_ruleset = 4;

  # HOSTNAME/PATH
  host.hostname = "${name}";
  path = "/usr/local/jails/containers/${name}";

  # NETWORK
  ip4.addr = 192.168.1.155;
  interface = em0;

  # MOUNT
  mount += "devfs     $path/compat/ubuntu/dev     devfs     rw  0 0";
  mount += "tmpfs     $path/compat/ubuntu/dev/shm tmpfs     rw,size=1g,mode=1777  0 0";
  mount += "fdescfs   $path/compat/ubuntu/dev/fd  fdescfs   rw,linrdlnk 0 0";
  mount += "linprocfs $path/compat/ubuntu/proc    linprocfs rw  0 0";
  mount += "linsysfs  $path/compat/ubuntu/sys     linsysfs  rw  0 0";
  mount += "/tmp      $path/compat/ubuntu/tmp     nullfs    rw  0 0";
  mount += "/home     $path/compat/ubuntu/home    nullfs    rw  0 0";
}
....

[TIP]
====
Клетка также может представлять конкретную идентичность ядра Linux программам, работающим внутри неё. Параметр `linux` выбирает, наследует ли клетка среду эмуляции Linux хост-системы (`inherit`) или получает собственную (`new`), а `linux.osname`, `linux.osrelease` и `linux.oss_version` задают имя ОС, релиз и версию OSS, которые видят бинарные файлы Linux. Например, `linux.osrelease=6.1.0` заставляет man:uname[1] и проверяющие версию программы Linux внутри клетки сообщать указанный релиз ядра вместо значения `compat.linux.osrelease` для всей хоста-системы.
====

Затем клетку можно запустить как обычно с помощью следующей команды:

[source, shell]
....
# service jail start ubuntu
....

Окружение Ubuntu можно запустить с помощью следующей команды:

[source, shell]
....
# jexec ubuntu chroot /compat/ubuntu /bin/bash
....

Дополнительную информацию можно найти в главе crossref:linuxemu[linuxemu, Двоичная совместимость с Linux].

[[jail-nat]]
== NAT и перенаправление портов для клеток

Клетка, которой присвоен частный адрес — на клонированном интерфейсе man:lo[4], таком как `lo1`, на частном мосту или на VNET epair во внутренней сети, — не имеет собственного маршрута в интернет. Хост должен транслировать трафик клетки в маршрутизируемый адрес, а для доступа к службе, работающей внутри такой клетки, извне — перенаправлять входящие соединения на клетку.

Сначала разрешите хосту пересылать пакеты между его интерфейсами. Включите это при загрузке и немедленно с помощью следующих команд:

[source, shell]
....
# sysrc gateway_enable="YES"
# sysctl net.inet.ip.forwarding=1
....

Для IPv6 используйте `ipv6_gateway_enable="YES"` и `net.inet6.ip6.forwarding=1`.

Затем настройте трансляцию сетевых адресов на хосте с помощью man:pf[4]. Следующий [.filename]#/etc/pf.conf# предоставляет каждой клетке в частной сети `192.168.0.0/24` исходящий доступ через внешний интерфейс хоста `em0` и публикует веб-сервер внутри клетки на порту 80 хоста:

[.programlisting]
....
ext_if = "em0"

# Translate outbound jail traffic to the host address
nat on $ext_if from 192.168.0.0/24 to any -> ($ext_if)

# Forward inbound web traffic to the jailed server
rdr on $ext_if proto tcp to port 80 -> 192.168.0.151
....

Цель `rdr` должна быть собственным адресом клетки.

Активируйте и запустите man:pf[4] с помощью следующей команды:

[source, shell]
....
# sysrc pf_enable="YES"
# service pf start
....

[NOTE]
====
FreeBSD 15.0 добавляет синтаксис в стиле OpenBSD для встроенных `nat-to` и `rdr-to`, который привязывает трансляцию к правилу `pass` или `match`, например `match out on $ext_if from 192.168.0.0/24 to any nat-to ($ext_if)`. Классические правила `nat on` и `rdr on`, показанные выше, работают как на FreeBSD 14.x, так и на 15.x.
====

Для полного рассмотрения man:pf[4] и man:ipfw[8], включая полные наборы правил и NAT на основе man:ipfw[8], см. crossref:firewalls[firewalls,Межсетевые экраны].

[[service-jails-config]]
== Сервисные клетки (Service Jails)

Сервисная клетка полностью настраивается через [.filename]#/etc/rc.conf# или man:sysrc[8]. Базовые системные сервисы готовы для работы в сервисных клетках. Они содержат строку конфигурации, которая включает сеть или снимает другие ограничения клеток. Базовые системные сервисы, которые не имеет смысла запускать внутри клеток, настроены так, чтобы не запускаться как сервисные клетки, даже если они включены в [.filename]#/etc/rc.conf#. Некоторые примеры таких сервисов — это сервисы, которые хотят монтировать или размонтировать что-то в методе start или stop, или только настраивают что-то, например маршрут, межсетевой экран или подобное.

Сторонние службы могут быть или не быть готовы к использованию в сервисных клетках. Чтобы проверить, готова ли служба к работе в сервисной клетке, можно использовать следующую команду:

[source, shell]
....
# grep _svcj_options /path/to/rc.d/servicename
....

Если вывод отсутствует, служба не готова к работе в клетке или не требует дополнительных привилегий, таких как, например, доступ к сети.

Если служба не готова к работе в клетке и требует доступа к сети, её можно подготовить, добавив необходимую конфигурацию в [.filename]#/etc/rc.conf#:

[source, shell]
....
# sysrc servicename_svcj_options=net_basic
....

Для всех возможных `_svcj_options` см. справочную страницу man:rc.conf[5].

Для включения сервисной клетки для указанной службы необходимо остановить службу и установить переменную `servicename_svcj` в значение YES. Чтобы поместить man:syslogd[8] в сервисную клетку, используйте следующую последовательность команд:

[source, shell]
....
# service syslogd stop
# sysrc syslogd_svcj=YES
# service syslogd start
....

Если переменная `servicename_svcj` изменяется, службу необходимо остановить перед внесением изменений. Если она не будет остановлена, rc-фреймворк не определит корректное состояние службы и не сможет выполнить запрошенное действие.

Сервисные клетки управляются только через man:rc.conf[5]/man:sysrc[8] и команду man:service[8]. Утилиты для работы с клетками, такие как man:jls[8], описанные в разделе crossref:jails[jail-management,Управление клетками], могут использоваться для проверки их работы, но команда man:jail[8] не предназначена для управления ими.

[[jail-management]]
== Управление клетками

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

[[list-running-jails]]
=== Список работающих клеток

Для вывода списка клеток, запущенных в хост-системе, можно использовать команду man:jls[8]:

[source, shell]
....
# jls
....

Вывод должен быть похож на следующий:

....
   JID  IP Address      Hostname                      Path
     1  192.168.1.151  classic                       /usr/local/jails/containers/classic
....

man:jls[8] поддерживает аргумент `--libxo`, который через библиотеку man:libxo[3] позволяет отображать данные в других форматах, таких как `JSON`, `HTML` и т.д.

Например, выполните следующую команду для получения вывода в формате `JSON`:

[source, shell]
....
# jls --libxo=json
....

Вывод должен быть похож на следующий:

....
{"__version": "2", "jail-information": {"jail": [{"jid":1,"ipv4":"192.168.1.151","hostname":"classic","path":"/usr/local/jails/containers/classic"}]}}
....

Стандартный вывод является лишь малой частью того, что может сообщить man:jls[8]. Добавьте флаг `-v` для подробной многострочной сводки по каждой клетке, включающей её состояние и назначенный идентификатор набора процессоров (cpuset ID):

[source, shell]
....
# jls -v
....

Используйте `-h` для вывода строки заголовка с названиями полей над значениями запрашиваемых параметров:

[source, shell]
....
# jls -h jid name path
....

Чтобы запросить конкретные параметры одной клетки, укажите её с помощью `-j` и перечислите параметры для вывода:

[source, shell]
....
# jls -j classic path host.hostname ip4.addr
....

Вывод должен быть похож на следующий:

....
/usr/local/jails/containers/classic classic 192.168.1.151
....

Добавьте `-d`, чтобы включить клетки, которые завершают работу и в противном случае скрыты, пока находятся в состоянии завершения:

[source, shell]
....
# jls -d
....

[[start-jail]]
=== Запуск, перезапуск и остановка клетки

man:service[8] используется для запуска, перезагрузки или остановки клетки на хосте.

Например, чтобы запустить клетку, выполните следующую команду:

[source, shell]
....
# service jail start jailname
....

Измените аргумент `start` на `restart` или `stop`, чтобы выполнить другие действия с клеткой.

Остановка клетки — это не просто завершение процессов внутри неё. Когда клетка останавливается, man:jail[8] сначала выполняет команды из её параметра `exec.stop` — обычно `/bin/sh /etc/rc.shutdown` — внутри клетки, чтобы её службы корректно завершились. Затем он отправляет `SIGTERM` всем процессам, всё ещё работающим в клетке, и ждёт до `stop.timeout` секунд (по умолчанию 10) их завершения. По истечении этого времени клетка удаляется, что завершает оставшиеся процессы сигналом `SIGKILL`. Если `stop.timeout` установлен в ноль, `SIGTERM` не отправляется, и клетка удаляется немедленно. Именно поэтому клетка с медленно останавливающимся демоном может казаться зависшей на десять секунд при остановке; увеличьте `stop.timeout` в [.filename]#/etc/jail.conf# для клетки, в которой работает база данных или другая служба, требующая больше времени для корректного завершения.

[[destroy-jail]]
=== Удаление клетки

Удаление клетки, это не просто остановка клетки с помощью man:service[8] и удаление каталога клетки и записи в [.filename]#/etc/jail.conf#.

FreeBSD очень серьезно относится к безопасности системы. По этой причине существуют определённые файлы, которые не может удалить даже пользователь root. Эта функциональность называется Флаги Файлов. В клетках, чьи файлы находятся на файловой системе UFS, эти флаги должны быть очищены до удаление каталога клетки.

Первым шагом является остановка нужной клетки с помощью выполнения следующей команды:

[source, shell]
....
# service jail stop classic
....

Второй шаг — удалить эти флаги с помощью man:chflags[1], выполнив следующую команду, где `classic` — имя удаляемой клетки:

[source, shell]
....
# chflags -R 0 /usr/local/jails/containers/classic
....

Третий шаг — удалить каталог, в котором находилась клетка:

[source, shell]
....
# rm -rf /usr/local/jails/containers/classic
....

Два шага выше относятся к клетке, хранящейся в файловой системе UFS. Клетка, поддерживаемая собственным набором данных ZFS — как созданная в crossref:jails[host-configuration-directories,Настройка дерева каталогов клетки] — удаляется вместо этого путем уничтожения набора данных. Уничтожение набора данных не блокируется файловыми флагами, поэтому шаг с man:chflags[1] в этом случае не требуется:

[source, shell]
....
# service jail stop classic
# zfs destroy zroot/jails/containers/classic
....

Добавьте `-r`, чтобы также уничтожить любые снимки, которые хранит набор данных.

[NOTE]
====
Тонкая клетка на ZFS, созданная клонированием снимка шаблона (см. crossref:jails[thin-jail,Тонкие клетки]), зависит от этого снимка. Пока существует любой клон, man:zfs[8] отказывается уничтожать снимок шаблона и выводит список зависимых клонов. Чтобы удалить шаблон, сначала уничтожьте каждую клетку, клонированную из него, или выполните `zfs promote` на клоне, чтобы он больше не зависел от шаблона.
====

Наконец, в любом случае удалите запись клетки в [.filename]#/etc/jail.conf# или в [.filename]#/etc/jail.conf.d#. Для тонкой клетки NullFS также удалите её файл [.filename]#.fstab# и её теперь пустой каталог точки монтирования.

[[handle-packages-jail]]
=== Работа с пакетами в клетке

Утилита man:pkg[8] поддерживает аргумент `-j` для управления пакетами, установленными внутри клетки.

Например, чтобы установить пакет package:www/nginx-lite[] в клетке, можно выполнить следующую команду *с хоста*:

[source, shell]
....
# pkg -j classic install nginx-lite
....

Для получения дополнительной информации о работе с пакетами в FreeBSD см. crossref:ports[ports,"Установка приложений: Пакеты и порты"].

[[jail-services-host]]
=== Управление службами в клетке с хост-системы

Службы внутри клетки можно включать и контролировать с хост-системы, не заходя сначала в клетку через man:jexec[8]. Как man:service[8], так и man:sysrc[8] принимают аргумент `-j`, который указывает целевую клетку по ID или имени и работает с конфигурацией man:rc[8] этой клетки.

Продолжая пример с пакетами выше, включите и запустите nginx в клетке `classic` непосредственно с хост-системы:

[source, shell]
....
# sysrc -j classic nginx_enable=YES
# service -j classic nginx start
....

Эти команды имеют тот же эффект, что и запуск man:sysrc[8] и man:service[8] через man:jexec[8], но они сохраняют единообразие администрирования и написания сценариев на стороне хост-системы с системой без клеток.

[[access-jail]]
=== Доступ к клетке

Как уже упоминалось выше, оптимальным является управление клетками из хост-системы, однако в клетку можно войти с помощью man:jexec[8].

В клетку можно войти, выполнив man:jexec[8] с хоста:

[source, shell]
....
# jexec -u root jailname
....

Флаг `-u` выбирает пользователя, от имени которого выполняется команда, разрешая имя через базу данных паролей *хост-системы*. Чтобы разрешить пользователя через *собственную* базу данных паролей клетки, используйте `-U`. Если команда не задана, как в данном случае, man:jexec[8] запускает оболочку входа этого пользователя внутри клетки. Добавьте `-l`, чтобы запустить её в чистой среде входа, сохраняющей только `HOME`, `SHELL`, `TERM` и `USER`, сбрасывающей `PATH` на [.filename]#/bin:/usr/bin# и ведущей себя как новый вход; `-d` задаёт рабочую директорию, по умолчанию это корень клетки.

При входе в клетку будет отображено сообщение, настроенное в man:motd[5].

[[execute-commands-jail]]
=== Выполнение команд в клетке

Для выполнения команды в клетке из хост-системы можно использовать man:jexec[8].

Например, чтобы остановить службу, работающую внутри клетки, будет выполнена команда:

[source, shell]
....
# jexec -l jailname service nginx stop
....

[[jail-view-processes]]
=== Просмотр процессов и сокетов в клетках с хост-системы

С хост-системы процессы и сокеты каждой клетки видны и могут фильтроваться по клетке.

man:ps[1] принимает `-J` для ограничения своего вывода одной клеткой, заданной по JID или имени:

[source, shell]
....
# ps -J classic
....

Вывод должен быть похож на следующий, где `J` в столбце `STAT` отмечает процесс, запущенный в клетке:

....
 PID TT  STAT    TIME COMMAND
2841  -  IsJ  0:00.02 /usr/sbin/syslogd -ss
2903  -  IsJ  0:00.00 nginx: master process /usr/local/sbin/nginx
....

Используйте `ps -J 0`, чтобы показать только процессы самой хост-системы. Ключевые слова `jail` и `jid` также можно добавить в формат вывода `-o`, чтобы показать, к какой клетке относится каждый процесс. Например, чтобы вывести список всех процессов на хост-системе вместе с их клеткой:

[source, shell]
....
# ps ax -o pid,jail,command
....

man:top[1] принимает `-J` для фильтрации своего живого отображения по одной клетке, что подразумевает `-j` и добавляет столбец `JID`:

[source, shell]
....
# top -J classic
....

man:sockstat[1] выводит список сокетов, принадлежащих клетке, с помощью `-j`, что является естественным способом проверить, какие порты демоны клетки фактически привязали после запуска в ней службы:

[source, shell]
....
# sockstat -4 -6 -l -j classic
....

Изнутри клетки картина обратная: процесс, заключенный в клетку, видит только процессы и сокеты своей собственной клетки и никогда не видит процессы или сокеты хоста или других клеток.

[[jail-manage-cli]]
=== Управление клетками напрямую с помощью утилиты jail

Представленная выше обёртка man:service[8] — это обычный способ запуска клеток при загрузке, но man:jail[8] также может создавать, изменять и удалять клетки напрямую, что полезно для написания скриптов и тестирования конфигурации без фреймворка rc.

Создайте настроенную клетку по имени:

[source, shell]
....
# jail -c classic
....

Удалите работающую клетку; это также удалит все дочерние клетки, которые она создала, и завершит процессы внутри неё:

[source, shell]
....
# jail -r classic
....

Флаг `-m` изменяет параметры уже запущенной клетки без её перезапуска. Например, чтобы добавить второй IPv4-адрес к запущенной клетке:

[source, shell]
....
# jail -m name=classic ip4.addr+=192.168.250.71
....

man:jail[8] отмечает, что «некоторые параметры нельзя изменить в работающей клетке»; на практике это включает `path` и `vnet`, для изменения которых требуется перезапуск клетки.

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

[source, shell]
....
# jail -e ,
....

[[jail-backup]]
=== Резервное копирование и перенос клетки

Клетка в значительной степени самодостаточна, поэтому её резервное копирование или перенос на другой хост в основном сводится к копированию её файлов и её конфигурации.

Остановите клетку сначала для получения консистентной копии; клетку на основе ZFS можно вместо этого сделать снимок во время её работы. Для клетки на собственном наборе данных ZFS выполните рекурсивный снимок и передайте его на целевой хост:

[source, shell]
....
# service jail stop classic
# zfs snapshot -r zroot/jails/containers/classic@migrate
# zfs send -R zroot/jails/containers/classic@migrate | ssh newhost zfs receive zroot/jails/containers/classic
....

Полная механика man:zfs-send[8] и man:zfs-receive[8], включая инкрементальные передачи, описана в crossref:zfs[zfs-zfs-send,Репликация].

Для клетки на файловой системе UFS используйте man:tar[1] с опцией `-p`, чтобы сохранить владельца, права доступа и флаги файлов:

[source, shell]
....
# tar -cpf classic.tar -C /usr/local/jails/containers classic
....

Извлеките архив как `root` на целевой системе с помощью `tar -xpf`, чтобы сохранились флаги. man:dump[8] и man:restore[8] являются альтернативой, когда клетка занимает выделенную файловую систему UFS; они работают с целыми файловыми системами, поэтому не применимы к структуре, используемой в этой главе, где все клетки являются каталогами в одной общей файловой системе.

Наконец, скопируйте блок строк, относящихся к клетке, из [.filename]#/etc/jail.conf# — или его файл в [.filename]#/etc/jail.conf.d# — на новый хост, настройте любые параметры, специфичные для хоста, такие как `interface`, `ip4.addr` или мост, используемый клеткой VNET, и запустите клетку там.

[CAUTION]
====
Целевая хост-система не должна запускать более старую версию FreeBSD, чем пользовательское окружение клетки, следуя тому же правилу, что клетка никогда не может запускать более новую версию, чем её хост-система. Тонкая клетка на NullFS не является автономной: её шаблон и скелет также должны существовать на целевой хост-системе, поэтому перенесите и их, или сначала преобразуйте клетку в толстую копию. Любые правила man:rctl[8], хранящиеся в [.filename]#/etc/rctl.conf#, должны быть перенесены отдельно.
====

[[jail-inside]]
== Администрирование изнутри клетки

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

Процесс может определить, находится ли он внутри клетки, прочитав sysctl `security.jail.jailed`, который равен `1` внутри клетки и `0` на хост-системе:

[source, shell]
....
# sysctl security.jail.jailed
security.jail.jailed: 1
....

Внутри клетки окружение намеренно ограничено:

* Вид на хост-систему скрыт. Процесс, находящийся в клетке, видит только процессы, сокеты и файловые системы своей собственной клетки, но никогда не видит процессы, сокеты и файловые системы хост-системы или других клеток.
* Состояние ядра в значительной степени доступно только для чтения. Настраиваемые параметры ядра не могут быть записаны изнутри клетки, man:kldload[8] и man:kldunload[8] не разрешены, а man:dmesg[8] и буфер сообщений ядра недоступны, если не установлен параметр `allow.read_msgbuf`.
* Привилегированные операции, которые пересекали бы границу клетки, запрещены, если только хост-система явно не разрешает их. Создание raw-сокетов — как того требуют man:ping[8] и man:traceroute[8] — требует `allow.raw_sockets` (см. описание параметра в разделе crossref:jails[jail-configuration-files,Файлы конфигурации клеток]), монтирование файловых систем требует `allow.mount` и его дочерних параметров для конкретных файловых систем, а man:chflags[1] по умолчанию отключен (см. crossref:jails[jail-upgrading,Обновление клеток]).

Горстка переменных sysctl, относящихся к каждой клетке, может быть изменена изнутри клетки без влияния на хост-систему, включая `kern.hostname`, `kern.domainname`, `kern.hostid`, `kern.hostuuid`, `kern.securelevel` и `security.bsd.suser_enabled`. Текущий набор доступных параметров клетки можно вывести с помощью man:sysctl[8]:

[source, shell]
....
# sysctl -d security.jail.param
....

Управление собственными службами клетки изнутри работает точно так же, как и в системе вне клетки: man:service[8] и man:sysrc[8] работают с собственной конфигурацией man:rc[8] клетки. Это внутриклеточный аналог показанных выше crossref:jails[jail-services-host,service -j и sysrc -j] из хост-системы.

[[jail-upgrading]]
== Обновление клетки

Обновление клеток FreeBSD гарантирует, что изолированные среды остаются безопасными, актуальными и соответствуют последним функциям и улучшениям, доступным в экосистеме FreeBSD.

Правильный инструмент зависит от того, как была установлена пользовательская среда клетки. Клетки, установленные из набора дистрибутивов (файл [.filename]#base.txz#, извлечённый в разделах создания, или созданный с помощью man:bsdinstall[8]), обновляются с хоста с помощью man:freebsd-update[8] через его флаг `-j`, как на FreeBSD 14.x, так и на 15.x. Клетки, установленные из базовых пакетов, доступных с FreeBSD 15.0, обновляются с помощью man:pkg[8] вместо этого; man:freebsd-update[8] к ним не применяется. Клетки, собранные из исходного кода, обновляются с помощью процедуры для исходного кода (`make installworld` и `make distribution`). Никогда не смешивайте эти механизмы в одной клетке.

[NOTE]
====
Поддержка наборов дистрибутивов планируется к удалению в FreeBSD 16, но будет продолжена, вместе с man:freebsd-update[8], на протяжении всего срока жизни стабильной ветки FreeBSD 15. Базовые пакеты предлагаются как технологическая превью в FreeBSD 15.0 и, как ожидается, станут стандартным методом в будущих выпусках, поэтому pkgbase — это перспективный путь.
====

[TIP]
====
В OpenZFS выполните снимок набора данных клетки перед обновлением, чтобы неудачное обновление можно было мгновенно отменить:

[source, shell]
....
# zfs snapshot zroot/jails/containers/classic@preupgrade
....

Если обновление не удалось, остановите клетку и выполните `zfs rollback zroot/jails/containers/classic@preupgrade`, чтобы восстановить предыдущее состояние. Для этого требуется, чтобы клетка была отдельным набором данных: тонкая клетка OpenZFS уже является таковым, но классическая клетка, созданная с помощью man:mkdir[1], не является, поэтому создайте её с помощью `zfs create` вместо этого. Для тонкой клетки NullFS сделайте снимок набора данных шаблона. man:zfs-rollback[8] откатывается только к самому последнему снимку, если не указан параметр `-r` для уничтожения более новых.
====

[[jails-updating]]
=== Обновление классической клетки или тонкой клетки с использованием снимков OpenZFS

Клетки *должны обновляться их операционной системы хоста*. В FreeBSD по умолчанию запрещено использование man:chflags[1] в клетке. Это предотвращает обновление некоторых файлов, поэтому обновление изнутри клетки завершится ошибкой.

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

[source, shell]
....
# freebsd-update -j classic fetch install
# service jail restart classic
....

Для обновления клетки до новой основной или промежуточной версии сначала обновите хост-систему, как описано в crossref:cutting-edge[freebsdupdate-upgrade,"Выполнение обновлений основной и промежуточной версий"]. После обновления и перезагрузки хоста можно обновить клетку.

[TIP]
====
В случае обновления с одной версии на другую проще создать новую клетку, чем выполнять полное обновление.
====

Например, для обновления с `классической` клетки до {rel-latest}-RELEASE выполните следующие команды на хосте:

[source, shell, subs=attributes]
....
# freebsd-update -j classic -r {rel-latest}-RELEASE upgrade
# freebsd-update -j classic install
# service jail restart classic
# freebsd-update -j classic install
# service jail restart classic
....

[NOTE]
====
Шаг `install` намеренно запускается дважды. Клетка не имеет собственного ядра (она работает на ядре хоста), поэтому здесь ничего не обновляет ядро. Первый `install` записывает новое пользовательское окружение в клетку; после того как клетка перезапускается на обновлённых файлах, второй `install` завершает замену компонентов, которые нельзя было изменить, пока их предыдущие версии всё ещё использовались.
====

Затем, если это было обновление основной версии, переустановите все установленные пакеты и перезапустите клетку снова. Это необходимо, потому что версия ABI изменяется при обновлении между основными версиями FreeBSD.

С хоста:

[source, shell]
....
# pkg -j classic upgrade -f
# service jail restart classic
....

[[jail-upgrade-zfs-template]]
=== Обновление тонкой клетки OpenZFS

Тонкие клетки, созданные в crossref:jails[creating-thin-jail-openzfs-snapshots,"Создание тонкой клетки с помощью снимков OpenZFS"], являются клонами OpenZFS снимка шаблона, но после создания клон независим от этого шаблона. Обновление шаблона *не* обновляет клетки, которые уже были клонированы из него, а обновление клона *не* обновляет шаблон. Поэтому существует два способа поддерживать эти клетки в актуальном состоянии.

Первый вариант — обновить каждый клон на месте с помощью man:freebsd-update[8], точно так же, как и для классической клетки, как описано в crossref:jails[jails-updating,"Обновление классической клетки или тонкой клетки с использованием снимков OpenZFS"]. Это самый простой подход для нескольких клеток, но каждый пропатченный клон отклоняется от шаблонного снимка: изменённые блоки дублируются для каждой клетки, что сокращает экономию места, а крупное обновление переписывает так много пользовательского окружения, что большая часть совместного использования теряется.

Второй вариант сохраняет совместное использование, обновляя шаблон и повторно клонируя его. Обновите шаблон до последнего уровня исправлений на хосте:

[source, shell, subs=attributes]
....
# freebsd-update -b /usr/local/jails/templates/{rel-latest}-RELEASE/ fetch install
....

[NOTE]
====
man:freebsd-update[8] с параметром `-b` читает текущую версию выпуска из `uname -r` хост-системы, а не из шаблона. Если шаблон работает на более старой версии выпуска, чем хост-система, добавьте `--currently-running <template-release>`, чтобы он применял исправления к версии выпуска шаблона, а не хоста-системы, как показано в crossref:jails[upgrading-thin-jail,"Обновление тонкой клетки с использованием NullFS"].
====

Затем создайте новый снимок и клонируйте будущие клетки из него:

[source, shell, subs=attributes]
....
# zfs snapshot zroot/jails/templates/{rel-latest}-RELEASE@base-p2
# zfs clone zroot/jails/templates/{rel-latest}-RELEASE@base-p2 zroot/jails/containers/newjail
....

Существующие клоны сохраняют базу, из которой они были созданы, до тех пор, пока они не будут пересозданы из нового снимка. Для обновления основной или дополнительной версии создайте новый шаблонный набор данных для нового выпуска, следуя разделу crossref:jails[creating-thin-jail-openzfs-snapshots,"Создание тонкой клетки с помощью снимков OpenZFS"], вместо обновления существующего шаблона на месте, и клонируйте новые клетки из его снимка `@base`.

[TIP]
====
Храните данные приложений и конфигурацию каждой клетки на отдельных наборах данных или в локальном хранилище клетки, чтобы повторное создание клетки из обновлённого снимка шаблона было дешёвым и предпочтительным по сравнению с отдельным обновлением каждого клона.
====

[[upgrading-thin-jail]]
=== Обновление тонкой клетки с использованием NullFS

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

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

[source, shell, subs=attributes]
....
# freebsd-update -b /usr/local/jails/templates/{rel-latest}-RELEASE-base/ fetch install
# service jail restart
....

Для обновления шаблона до новой основной или промежуточной версии сначала обновите хост-систему, как описано в crossref:cutting-edge[freebsdupdate-upgrade,"Выполнение обновлений основной и промежуточной версий"]. После обновления и перезагрузки хост-системы можно обновить шаблон.

Поскольку хост-система была обновлёна первой, man:freebsd-update[8] с `-b` в противном случае обнаружил бы новый выпуск хост-системы, а не более старый выпуск шаблона: `-b` считывает текущую версию из `uname -r` хост-системы, в отличие от `-j`, который определяет собственную пользовательскую среду клетки. Передайте `--currently-running <release-шаблона>`, чтобы шаблон был обновлён с его фактического выпуска. Следующий пример обновляет шаблон, работающий на 14.2-RELEASE, до 14.3-RELEASE; подставьте применимые выпуски:

[source, shell]
....
# freebsd-update -b /usr/local/jails/templates/14.2-RELEASE-base/ --currently-running 14.2-RELEASE -r 14.3-RELEASE upgrade
# freebsd-update -b /usr/local/jails/templates/14.2-RELEASE-base/ install
# service jail restart
# freebsd-update -b /usr/local/jails/templates/14.2-RELEASE-base/ install
# service jail restart
....

[[jail-upgrade-pkgbase]]
=== Обновление клеток с pkgbase

Клетки, установленные из базовых пакетов, доступных с FreeBSD 15.0, обновляются с помощью man:pkg[8] через репозиторий `FreeBSD-base`, а не с помощью man:freebsd-update[8].

Репозиторий `FreeBSD-base` определён в [.filename]#/etc/pkg/FreeBSD.conf#, но по умолчанию отключён в FreeBSD 15.0, поэтому включите его перед обновлением.

С хост-системы укажите man:pkg[8] на корневой каталог клетки с помощью `-r` (`--rootdir`):

[source, shell]
....
# pkg -r /usr/local/jails/containers/pkgjail update
# pkg -r /usr/local/jails/containers/pkgjail upgrade
# service jail restart pkgjail
....

Тот же самый апгрейд можно выполнить изнутри клетки с помощью man:jexec[8] и обычной команды `pkg upgrade`:

[source, shell]
....
# jexec -l pkgjail pkg upgrade
# service jail restart pkgjail
....

man:pkg[8] выполняет трехстороннее слияние размеченных конфигурационных файлов в [.filename]#/etc#. Когда слияние не может быть завершено автоматически, он оставляет новую версию как [.filename]#<file>.pkgnew#, а когда он забирает файл, который ранее не был упакован, он сохраняет старую копию как [.filename]#<file>.pkgsave#. Проверьте [.filename]#/etc# после обновления и вручную согласуйте любые такие файлы. Не запускайте man:etcupdate[8] в системе pkgbase; man:pkg[8] сам обрабатывает слияние конфигураций. Смотрите crossref:cutting-edge[cutting-edge,"Обновление и модернизация FreeBSD"] для описания рабочего процесса с базовыми пакетами на стороне хоста.

[[jail-resource-limits]]
== Ограничения ресурсов клетки

Управление ресурсами хост-системы, которые использует клетка — это задача, которую должен учитывать системный администратор.

Используйте man:rctl[8] для управления ресурсами хост-системы, которые клетка может использовать.

[TIP]
====
Настройка `kern.racct.enable` должна быть включена в файле [.filename]#/boot/loader.conf#.
====

Синтаксис для ограничения ресурсов клетки выглядит следующим образом:

[.programlisting]
....
rctl -a jail:<jailname>:resource:action=amount[/per]
....

Необязательный компонент `per` не является процентным соотношением; это учетная единица, к которой применяется ограничение: одна из `process`, `user`, `loginclass` или `jail`. Например, `deny=2G/jail` ограничивает сумму для всей клетки, тогда как `deny=2G/process` применяет то же значение к каждому процессу по отдельности. Процентное соотношение имеет смысл только для ресурса `pcpu`, количество которого выражается в процентах от одного ядра процессора.

Например, чтобы ограничить максимальный объём оперативной памяти, доступной для клетки, выполните следующую команду:

[source, shell]
....
# rctl -a jail:classic:memoryuse:deny=2G
....

Чтобы ограничение сохранялось после перезагрузки хост-системы, необходимо добавить правило в файл [.filename]#/etc/rctl.conf# следующим образом:

[.programlisting]
....
jail:classic:memoryuse:deny=2G/jail
....

man:rctl[8] может ограничивать гораздо больше, чем память. Ресурсы, наиболее значимые для клетки, включают `pcpu` (процент одного ядра ЦП), `maxproc` (количество процессов), `openfiles` (открытые файловые дескрипторы), `vmemoryuse` (адресное пространство) и `swapuse` (пространство подкачки). Каждый из них настраивается одинаково:

[source, shell]
....
# rctl -a jail:classic:pcpu:deny=80
# rctl -a jail:classic:maxproc:deny=1000
# rctl -a jail:classic:openfiles:deny=5000
# rctl -a jail:classic:vmemoryuse:deny=4G
....

Пропускная способность диска ограничивается параметрами `readbps` и `writebps`, но эти ресурсы не поддерживают действие `deny`; вместо этого объединяйте их с `throttle`:

[source, shell]
....
# rctl -a jail:classic:writebps:throttle=1m
....

Действие после ресурса определяет, что происходит при достижении лимита: `deny` блокирует операцию, `log` записывает её в системный журнал, а `devctl` отправляет уведомление через man:devd[8].

Сообщите о текущем использовании ресурсов клетки с помощью флага `-u`, добавив `-h` для единиц, удобных для чтения человеком:

[source, shell]
....
# rctl -hu jail:classic
....

Перечислите правила, которые применяются к клетке:

[source, shell]
....
# rctl jail:classic
....

Удалите каждое правило, которое соответствует клетке, с помощью `-r`:

[source, shell]
....
# rctl -r jail:classic
....

Дополнительная информация об ограничениях ресурсов доступна в главе о безопасности в разделе crossref:security[security-resourcelimits,"Ограничения ресурсов"].

[[jail-cpu-pinning]]
=== Привязка CPU с помощью cpuset

man:rctl[8] с ограничением `pcpu` определяет, сколько времени ЦП может потреблять клетка, но она не привязывает клетку к конкретным ядрам; man:cpuset[1] вместо этого назначает конкретные ядра.

Ограничить работающую клетку набором CPU по её имени или JID:

[source, shell]
....
# cpuset -l 0-3 -j classic
....

Запросить ядра, которые клетка в настоящее время может использовать, с помощью `-g`:

[source, shell]
....
# cpuset -g -j classic
....

Назначенный набор также отображается в столбце `cpuset` команды `jls -v`. В файле `jail.conf` нет параметра для привязки к процессору, поэтому закрепите клетку при запуске, выполнив man:cpuset[1] из хука `exec.created` или `exec.poststart`, оба из которых выполняются в среде хоста:

[.programlisting]
....
exec.poststart = "cpuset -l 0-3 -j classic";
....

[[limiting-disk-space]]
=== Ограничение дискового пространства

man:rctl[8] не имеет ресурса для файлового пространства. Когда каждая клетка находится в собственном наборе данных ZFS, ограничьте и гарантируйте использование дискового пространства с помощью квот и резервирований ZFS вместо этого:

[source, shell]
....
# zfs set quota=20G zroot/jails/containers/classic
# zfs set reservation=5G zroot/jails/containers/classic
....

Для этого требуется, чтобы клетка была собственным набором данных. Созданные ранее тонкие клетки ZFS уже таковыми являются, но процедура Классической Клетки создает каталог клетки с помощью `mkdir` внутри общего набора данных [.filename]#containers#, поэтому сначала создайте его с помощью `zfs create`, чтобы эти команды применялись. См. crossref:jails[host-configuration-directories,"Настройка дерева каталогов клетки"] для структуры наборов данных и man:zfs-set[8] для свойств `quota` и `reservation`.

[[jail-more-options]]
== Дополнительные параметры клетки

man:jail[8] предоставляет гораздо больше параметров, чем используется в примерах из этой главы. Клетка является сильной границей изоляции, но не абсолютной. Каждое послабление `allow.*` обменивает изоляцию на возможности и приближает привилегированных пользователей клетки на шаг ближе к привилегиям `root` на хост-системе. Предоставляйте клетке только те разрешения, которые действительно необходимы для выполняемой в ней рабочей нагрузки, а всё остальное оставляйте в более ограничительном режиме по умолчанию. Полный список приведён в man:jail[8] и man:jail.conf[5]; параметры ниже — это те, к которым администраторы обращаются чаще всего.

[[allow-permissions]]
=== Разрешения allow.*

Параметры `allow.*` являются логическими значениями, которые предоставляют отдельные привилегии клетке. Согласно man:jail[8], "За исключением allow.set_hostname и allow.reserved_ports, эти логические параметры по умолчанию выключены", поэтому клетка запускается в заблокированном состоянии, и каждая возможность должна быть предоставлена явно.

.Общие параметры `allow.*`
[cols="2,1,3", options="header"]
|===
| Параметр | По умолчанию | Эффект

| `allow.set_hostname`
| On
| Клетка может изменить собственное имя хоста.

| `allow.reserved_ports`
| On
| `root` в клетке может открывать (делать bind) зарезервированные порты ниже 1024.

| `allow.raw_sockets`
| Off
| Включает «сырые» (raw) сокеты, чтобы man:ping[8] и man:traceroute[8] работали внутри клетки.

| `allow.chflags`
| Off
| `root` в клетке может управлять системными файловыми флагами так же, как и на хост-системе.

| `allow.mount`
| Off
| Клетка может монтировать и размонтировать файловые системы, вместе с дочерними опциями `allow.mount.*`.

| `allow.quotas`
| Off
| Клетка может администрировать квоты файловой системы.

| `allow.read_msgbuf`
| Off
| Процессы в клетках могут читать буфер сообщений ядра с помощью man:dmesg[8].

| `allow.socket_af`
| Off
| Разрешает семейства адресов сокетов, отличные от IPv4, IPv6, локальных и маршрутных.

| `allow.mlock`
| Off
| Процессы в клетках могут блокировать физическую память с помощью man:mlock[2].

| `allow.unprivileged_proc_debug`
| Off
| Непривилегированные пользователи могут использовать средства отладки для своих собственных процессов.

| `allow.vmm`
| Off
| Клетка может использовать гипервизор man:vmm[4] для запуска гостевых систем man:bhyve[8].

| `allow.nfsd`
| Off
| Клетка может запускать NFS-сервер.
|===

Взвесьте каждое послабление относительно того, что оно открывает: `allow.raw_sockets` позволяет процессам в клетке подделывать пакеты, `allow.chflags` позволяет `root` в клетке снимать флаги неизменяемости и только для добавления, на которые полагается хост-система, а `allow.mount` открывает пространство имён файловой системы.

Запуск NFS-сервера внутри клетки, отмеченный в примечаниях к выпуску FreeBSD 14.0, требует установки параметра `allow.nfsd`, VNET-клетки, корневой каталог которой сам является точкой монтирования файловой системы, а также параметра `enforce_statfs`, не установленного в значение `0`.

[[system-v-ipc]]
=== IPC System V (межпроцессное взаимодействие)

Базы данных и другое программное обеспечение, которое полагается на разделяемую память System V, семафоры и очереди сообщений, управляются по модулям с помощью параметров `sysvmsg`, `sysvsem` и `sysvshm`. Каждый из них принимает одно из трёх значений: `inherit` делает все объекты IPC узла видимыми для клетки, `new` предоставляет клетке собственное частное пространство имён ключей, а `disable` приводит к сбою этих системных вызовов.

Для классического случая запуска PostgreSQL в клетке, выбор `new` является правильным, предоставляя базе данных собственное пространство имён разделяемой памяти, не раскрывая пространство хост-системы:

[.programlisting]
....
sysvmsg = new;
sysvsem = new;
sysvshm = new;
....

Устаревший параметр `allow.sysvipc` больше не используется. Он эквивалентен установке всех трех параметров модуля в значение `inherit`, что разделяет общесистемное пространство имен IPC с клеткой, поэтому предпочтительнее использовать указанные выше параметры для каждого модуля.

[[mounting-filesystems]]
=== Монтирование файловых систем

`allow.mount` позволяет клетке монтировать файловые системы, а семейство дочерних параметров выбирает, какие именно типы: `allow.mount.devfs`, `allow.mount.fdescfs`, `allow.mount.fusefs`, `allow.mount.nullfs`, `allow.mount.procfs`, `allow.mount.linprocfs`, `allow.mount.linsysfs`, `allow.mount.tmpfs` и `allow.mount.zfs`. Согласно man:jail[8], каждый дочерний параметр «действует только вместе с `allow.mount` и только когда `enforce_statfs` установлен в значение ниже 2». Удобные параметры `mount.devfs`, `mount.fdescfs` и `mount.procfs` автоматически монтируют соответствующие файловые системы для клетки, а `mount.fstab` указывает на файл fstab с дополнительными точками монтирования.

Параметр `enforce_statfs` управляет как тем, какие точки монтирования может видеть клетка, так и, согласно приведенному выше правилу, тем, действуют ли разрешения `allow.mount.*`. Он принимает три значения:

.`enforce_statfs` значения
[cols="1,4", options="header"]
|===
| Значение | Точки монтирования, видимые клетке

| `0`
| Все точки монтирования на хост-системе, без каких-либо ограничений.

| `1`
| Видны только точки монтирования ниже корневого каталога клетки.

| `2` (по умолчанию)
| Видна только единственная точка монтирования, где расположен корневой каталог клетки.
|===

Поскольку для прав `allow.mount.*` требуется значение ниже `2`, клетка, которой необходимо монтировать собственные файловые системы, обычно настраивается с параметром `enforce_statfs = 1`.

[[per-jail-securelevel]]
=== Уровень безопасности (securelevel) для каждой клетки

Параметр `securelevel` устанавливает значение `kern.securelevel` для клетки и повышает уровень защиты ядра от изменений флагов и устройств внутри клетки. Согласно man:jail[8], «клетка никогда не имеет более низкого уровня securelevel, чем её родительская система, но, установив этот параметр, она может иметь более высокий». Таким образом, клетка, обращённая к сети, может работать при ужесточённом `securelevel = 3`, в то время как хост-система остаётся на уровне по умолчанию.

[[hierarchical-jails]]
=== Иерархичские клетки (Hierarchical Jails)

Клетка сама может создавать дочерние клетки, именно так строятся вложенные или иерархические структуры клеток. Параметр `children.max` задаёт, сколько дочерних клеток может создать клетка; по умолчанию он равен `0`, что означает отсутствие разрешения на создание дочерних клеток, а доступный только для чтения параметр `children.cur` сообщает, сколько их существует в данный момент. Дочерняя клетка никогда не может превысить привилегии своего родителя: согласно man:jail[8], «Процессы, находящиеся в клетке, не имеют права предоставлять большие полномочия, чем те, которые даны им самим». Это делает возможным запуск менеджера клеток внутри клетки.

[[empty-persistent-jails]]
=== Пустые и постоянные клетки

По умолчанию клетка существует только пока в ней есть запущенные процессы; когда её последний процесс завершается, клетка исчезает. Установка параметра `persist`, по словам man:jail[8], «позволяет клетке существовать без каких-либо процессов». Именно это поддерживает клетку в живых после её создания пустой и до того, как в неё будет что-либо помещено, а также закрепляет VNET-клетку, которая хранит только сетевое состояние. Клетка может быть создана пустой и поддерживаться в живых для экспериментов:

[source, shell]
....
# jail -c name=test path=/rescue persist
....

Сбросьте флаг с помощью `nopersist`, чтобы клетка могла быть удалена после того, как снова станет неактивной.

[[jail-osrelease]]
=== Представление другого выпуска ОС

Пользовательское окружение клетки может быть старше, чем ядро хост-системы, но инструменты внутри нее все равно читают версию хост-системы через man:uname[1], если не указано иное. Параметр `osrelease` задает строку, сообщаемую через `kern.osrelease` и `uname -r` клетки, а `osreldate` задает число, сообщаемое через `kern.osreldate` и `uname -K`. Установка их таким образом, чтобы более старое пользовательское окружение сообщало о собственной версии, обеспечивает корректную работу man:uname[1], потребителей man:sysctl[8] и инструментов сборки, определяющих версию, включая man:freebsd-update[8], запускаемый внутри клетки. Управление пакетами не затрагивается ни в ту, ни в другую сторону, потому что man:pkg[8] получает свой ABI из ELF-заметок собственных бинарных файлов клетки, а не из man:uname[1]. Это дополняет правило, согласно которому пользовательское окружение клетки никогда не может быть новее, чем ядро хоста; см. crossref:jails[jail-upgrading,"Обновление клеток"].

[[jails-zfs]]
== Клетки и ZFS

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

Хост-система сохраняет право собственности на набор данных и делегирует клетке только управление делегированным деревом и его потомками. Свойство `jailed`, подкоманда `zfs jail` и взаимодействие между ними описаны в crossref:zfs[zfs-zfs-jail,ZFS и клетки].

[[delegating-dataset-manually]]
=== Ручное делегирование набора данных

Этот метод работает на всех поддерживаемых выпусках FreeBSD.

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

[source, shell]
....
# zfs create zroot/jails/data
# zfs set jailed=on zroot/jails/data
....

Установка свойства `jailed` сообщает ZFS, что набор данных будет управляться изнутри клетки, поэтому хост-система автоматически перестаёт его монтировать.

Далее, разрешите клетке монтировать файловые системы ZFS и присоединять набор данных при создании клетки. Добавьте следующее в раздел клетки в [.filename]#/etc/jail.conf#:

[.programlisting]
....
allow.mount;
allow.mount.zfs;
enforce_statfs = 1;
exec.created += "zfs jail ${name} zroot/jails/data";
....

Права `allow.mount.zfs` действуют только вместе с `allow.mount` и значением `enforce_statfs` ниже 2; см. crossref:jails[jail-more-options,More Jail Options] для прав на монтирование и их взаимодействия с `enforce_statfs`. Поскольку делегированный набор данных отсоединяется при остановке клетки, хук `exec.created` повторно присоединяет его при каждом запуске, где `${name}` расширяется до имени клетки. То же присоединение можно выполнить к уже запущенной клетке с помощью команды `zfs jail <jid|name> zroot/jails/data`.

[NOTE]
====
Клетке требуется доступ к [.filename]#/dev/zfs#. Стандартный набор правил devfs для клеток (`devfsrules_jail`, набор правил 4) уже открывает его, поэтому для делегирования ZFS не требуется пользовательский набор правил devfs.
====

Внутри клетки смонтируйте делегированные наборы данных и управляйте ими как любыми другими:

[source, shell]
....
# zfs mount -a
# zfs create zroot/jails/data/work
# zfs snapshot zroot/jails/data/work@monday
....

root-пользователь в клетке может создавать, делать снимки, клонировать и откатывать наборы данных в делегированном дереве, но свойство `quota` делегированного набора данных может быть установлено только с хоста-системы.

[[attaching-datasets-automatically]]
=== Автоматическое подключение наборов данных

[NOTE]
====
Параметр `zfs.dataset` требует FreeBSD 15.0. Он отсутствует в man:jail[8] для FreeBSD 14.3.
====

Начиная с FreeBSD 15.0, man:jail[8] может самостоятельно подключать наборы данных, устраняя необходимость в ручных шагах `jailed` и `zfs jail`. Перечислите наборы данных для делегирования в параметре `zfs.dataset`:

[.programlisting]
....
allow.mount;
allow.mount.zfs;
enforce_statfs = 1;
zfs.dataset = "zroot/jails/data";
....

man:jail[8] присоединяет каждый перечисленный набор данных при запуске клетки, поэтому показанный выше хук `exec.created` больше не требуется. Этот параметр требует, чтобы было установлено значение `allow.mount.zfs`.

Для неустаревших менеджеров клеток, которые еще не понимают `zfs.dataset`, стартовый сценарий rc.d/jail также учитывает устаревшую переменную `jail_<jailname>_zfs_dataset` в [.filename]#/etc/rc.conf#.

[[accessing-snapshots-inside-jail]]
=== Доступ к снимкам изнутри клетки

Параметр `zfs.mount_snapshot`, доступный в FreeBSD 14.x и 15.x, позволяет пользователям внутри клетки получать доступ к содержимому снимков без делегирования всего набора данных. Когда он установлен в значение `1`, пользователи клетки могут читать содержимое снимков файловой системы в её каталоге [.filename]#.zfs/snapshot#:

[.programlisting]
....
zfs.mount_snapshot = 1;
....

Это удобно для восстановления отдельных файлов из снимков, сделанных на хост-системе. Если также установлено `allow.mount.zfs`, снимки могут быть дополнительно смонтированы внутри клетки. Свойство `snapdir` набора данных управляет тем, будет ли виден каталог [.filename]#.zfs#.

[WARNING]
====
Делегированный набор данных в сочетании с `allow.mount.zfs` позволяет root-пользователю внутри клетки устанавливать связанные с монтированием свойства ZFS и монтировать файловые системы внутри клетки. Предоставляйте эти разрешения обдуманно и только тем клеткам, которым доверено управлять собственным хранилищем.
====

[[jail-managers-and-containers]]
== Менеджеры клеток и контейнеры

Как уже объяснялось ранее, каждый тип клетки FreeBSD может быть создан и настроен вручную, но в FreeBSD также существуют сторонние утилиты для упрощения настройки и администрирования.

Ниже приведён неполный список различных менеджеров клеток FreeBSD:

.Менеджеры клеток
[options="header", cols="1,1,1,1"]
|===
| Имя | Лицензия | Пакет | Documentation

| BastilleBSD
| BSD-3
| package:sysutils/bastille[]
| link:https://bastille.readthedocs.io/ru/latest/[Документация]

| pot
| BSD-3
| package:sysutils/pot[]
| link:https://pot.pizzamig.dev/[Документация]

| cbsd
| BSD-2
| package:sysutils/cbsd[]
| link:https://github.com/cbsd/cbsd[Документация]

| AppJail
| BSD-3
| package:sysutils/appjail[], для разработки package:sysutils/appjail-devel[]
| link:https://github.com/DtxdF/AppJail#getting-started[Документация]

| iocage
| BSD-2
| package:sysutils/iocage[]
| link:https://freebsd.github.io/iocage/[Документация]

| ezjail
| link:https://erdgeist.org/beerware.html[Пивная Лицензия]
| package:sysutils/ezjail[]
| link:https://erdgeist.org/arts/software/ezjail/[Документация]

|===

FreeBSD публикует официальные OCI-образы базовой системы, начиная с FreeBSD 14.3-RELEASE, а OCI-контейнеры (стиль Docker), которые построены на той же технологии клеток и управляются с помощью инструментария Podman, рассматриваются в главе crossref:containers[containers,Контейнеры], которая дополняет обычные клетки, описанные здесь.
