# 17.5.瘦 jail

虽然瘦 jail 使用与厚 jail 相同的技术，但创建过程是不同的。瘦 jail 可以通过 OpenZFS 快照或使用模板和 NullFS 创建。使用 OpenZFS 快照和模板结合 NullFS 相比经典的 jail 有一些优势，例如可以更快速地从快照中创建，或者能够通过 NullFS 更新多个 jail。

## 17.5.1. 使用 OpenZFS 快照创建瘦 jail

由于 FreeBSD 和 OpenZFS 的良好集成，通过 OpenZFS 快照创建新的瘦 jail 非常容易。

要通过 OpenZFS 快照创建瘦 jail，第一步是创建 jail 目录树，按照[设置 Jail 目录树](https://docs.freebsd.org/en/books/handbook/jails/#host-configuration-directories)中的说明进行操作。

接下来，创建一个模板。模板仅用于创建新的 jail。因此，它们以“只读”模式创建，以便从不变的基础创建 jail。

要为模板创建数据集，执行以下命令：

```sh
# zfs create -p zroot/jails/templates/14.2-RELEASE
```

然后执行以下命令下载用户空间：

```sh
# fetch https://download.freebsd.org/ftp/releases/amd64/amd64/14.2-RELEASE/base.txz -o /usr/local/jails/media/14.2-RELEASE-base.txz
```

下载完成后，需要通过以下命令将内容解压到模板目录中：

```sh
# tar -xf /usr/local/jails/media/14.2-RELEASE-base.txz -C /usr/local/jails/templates/14.2-RELEASE --unlink
```

在模板目录中提取用户空间后，需要通过以下命令将时区和 DNS 服务器文件复制到模板目录中：

```sh
# cp /etc/resolv.conf /usr/local/jails/templates/14.2-RELEASE/etc/resolv.conf
# cp /etc/localtime /usr/local/jails/templates/14.2-RELEASE/etc/localtime
```

接下来的步骤是通过执行以下命令更新到最新的补丁级别：

```sh
# freebsd-update -b /usr/local/jails/templates/14.2-RELEASE/ fetch install
```

更新完成后，模板就准备好了。

要从模板创建 OpenZFS 快照，执行以下命令：

```sh
# zfs snapshot zroot/jails/templates/14.2-RELEASE@base
```

待创建了 OpenZFS 快照，就可以使用 OpenZFS 克隆功能创建无限多个 jail。

要创建一个名为 `thinjail` 的瘦 jail，执行以下命令：

```sh
# zfs clone zroot/jails/templates/14.2-RELEASE@base zroot/jails/containers/thinjail
```

最后一步是配置 jail。需要在 **/etc/jail.conf** 或 **jail.conf.d** 配置文件中添加该 jail 的条目。

例如，配置可能如下所示：

```ini
thinjail {
  # 启动/日志记录
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # 权限
  allow.raw_sockets;
  exec.clean;
  mount.devfs;

  # 主机名/路径
  host.hostname = "${name}";
  path = "/usr/local/jails/containers/${name}";

  # 网络
  ip4 = inherit;
  interface = em0;
}
```

执行以下命令启动 jail：

```sh
# service jail start thinjail
```

有关如何管理 jail 的更多信息，请参阅[Jail 管理](https://docs.freebsd.org/en/books/handbook/jails/#jail-management)章节。

## 17.5.2. 使用 NullFS 创建瘦 jail

通过使用瘦 jail 技术并利用 NullFS 有选择性地共享主机系统中的特定目录，可以减少系统文件的重复，从而创建一个瘦 jail。

第一步是创建用于保存模板的数据集。如果使用 OpenZFS，请执行以下命令：

```sh
# zfs create -p zroot/jails/templates/14.2-RELEASE-base
```

如果使用 UFS，请执行以下命令：

```sh
# mkdir /usr/local/jails/templates/14.2-RELEASE-base
```

然后执行以下命令下载用户空间：

```sh
# fetch https://download.freebsd.org/ftp/releases/amd64/amd64/14.2-RELEASE/base.txz -o /usr/local/jails/media/14.2-RELEASE-base.txz
```

下载完成后，需要通过以下命令将内容解压到模板目录中：

```sh
# tar -xf /usr/local/jails/media/14.2-RELEASE-base.txz -C /usr/local/jails/templates/14.2-RELEASE-base --unlink
```

待用户空间提取到模板目录中，就需要通过以下命令将时区和 DNS 服务器文件复制到模板目录中：

```sh
# cp /etc/resolv.conf /usr/local/jails/templates/14.2-RELEASE-base/etc/resolv.conf
# cp /etc/localtime /usr/local/jails/templates/14.2-RELEASE-base/etc/localtime
```

将文件移动到模板中后，接下来的步骤是通过以下命令更新到最新的补丁级别：

```sh
# freebsd-update -b /usr/local/jails/templates/14.2-RELEASE-base/ fetch install
```

除了基础模板外，还需要创建一个目录来存放 `skeleton`。一些目录将从模板复制到 `skeleton` 中。

如果使用 OpenZFS，请执行以下命令来创建 `skeleton` 的数据集：

```sh
# zfs create -p zroot/jails/templates/14.2-RELEASE-skeleton
```

如果使用 UFS，请执行以下命令：

```sh
# mkdir /usr/local/jails/templates/14.2-RELEASE-skeleton
```

然后创建 `skeleton` 目录。`skeleton` 目录将存放 jail 的本地目录。

执行以下命令创建这些目录：

```sh
# mkdir -p /usr/local/jails/templates/14.2-RELEASE-skeleton/home
# mkdir -p /usr/local/jails/templates/14.2-RELEASE-skeleton/usr
# mv /usr/local/jails/templates/14.2-RELEASE-base/etc /usr/local/jails/templates/14.2-RELEASE-skeleton/etc
# mv /usr/local/jails/templates/14.2-RELEASE-base/usr/local /usr/local/jails/templates/14.2-RELEASE-skeleton/usr/local
# mv /usr/local/jails/templates/14.2-RELEASE-base/tmp /usr/local/jails/templates/14.2-RELEASE-skeleton/tmp
# mv /usr/local/jails/templates/14.2-RELEASE-base/var /usr/local/jails/templates/14.2-RELEASE-skeleton/var
# mv /usr/local/jails/templates/14.2-RELEASE-base/root /usr/local/jails/templates/14.2-RELEASE-skeleton/root
```

接下来的步骤是通过执行以下命令创建指向 `skeleton` 的符号链接：

```sh
# cd /usr/local/jails/templates/14.2-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` 准备好后，需要将数据复制到 jail 目录中。

如果使用 OpenZFS，可以使用 OpenZFS 快照轻松创建所需数量的 jail，执行以下命令：

```sh
# zfs snapshot zroot/jails/templates/14.2-RELEASE-skeleton@base
# zfs clone zroot/jails/templates/14.2-RELEASE-skeleton@base zroot/jails/containers/thinjail
```

如果使用 UFS，可以使用 [cp(1)](https://man.freebsd.org/cgi/man.cgi?query=cp\&sektion=1\&format=html) 程序，执行以下命令：

```sh
# cp -R /usr/local/jails/templates/14.2-RELEASE-skeleton /usr/local/jails/containers/thinjail
```

然后创建一个目录，用于挂载基础模板和 skeleton：

```sh
# mkdir -p /usr/local/jails/thinjail-nullfs-base
```

在 **/etc/jail.conf** 或 **jail.conf.d** 文件中添加 jail 条目，如下所示：

```ini
thinjail {
  # 启动/日志记录
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # 权限
  allow.raw_sockets;
  exec.clean;
  mount.devfs;

  # 主机名/路径
  host.hostname = "${name}";
  path = "/usr/local/jails/${name}-nullfs-base";

  # 网络
  ip4.addr = 192.168.1.153;
  interface = em0;

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

然后创建 **/usr/local/jails/thinjail-nullfs-base.fstab** 文件，内容如下：

```sh
/usr/local/jails/templates/14.2-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
```

执行以下命令启动 jail：

```sh
# service jail start thinjail
```

## 17.5.3. 创建 VNET Jail

FreeBSD VNET Jail 拥有独立的网络栈，包括接口、IP 地址、路由表和防火墙规则。

创建 VNET jail 的第一步是创建 [bridge(4)](https://man.freebsd.org/cgi/man.cgi?query=bridge\&sektion=4\&format=html)，执行以下命令：

```sh
# ifconfig bridge create
```

输出应该类似于以下内容：

```sh
bridge0
```

创建好 `bridge` 后，接下来需要将其附加到 `em0` 接口，并启动它们，执行以下命令：

```sh
# ifconfig bridge0 addm em0 up
# ifconfig em0 up
```

为了使此设置在重启后生效，将以下行添加到 **/etc/rc.conf** 中：

```sh
defaultrouter="192.168.1.1"
cloned_interfaces="bridge0"
ifconfig_bridge0="inet 192.168.1.150/24 addm em0 up"
ifconfig_em0="up"
```

有关桥接的更多信息，请参阅 [Network Bridging](https://docs.freebsd.org/en/books/handbook/advanced-networking/#network-bridging)。

接下来的步骤是根据上述内容创建 jail。

可以使用 [经典 Jail（厚 Jail）](https://docs.freebsd.org/en/books/handbook/jails/#classic-jail) 或 [瘦 Jail](https://docs.freebsd.org/en/books/handbook/jails/#thin-jail) 的方法。唯一不同的是 **/etc/jail.conf** 文件中的配置。

在这个示例中，将使用路径 **/usr/local/jails/containers/vnet** 来表示创建的 jail。

以下是 VNET jail 的示例配置：

```ini
vnet {
  # 启动/日志记录
  exec.consolelog = "/var/log/jail_console_${name}.log";

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

  # 路径/主机名
  path = "/usr/local/jails/containers/${name}";
  host.hostname = "${name}";

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

  # 网络/接口
  $id = "154"; ①
  $ip = "192.168.1.${id}/24";
  $gateway = "192.168.1.1";
  $bridge = "bridge0"; ②
  $epair = "epair${id}";

  # 添加到 bridge 接口
  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";
}
```

* ① 表示 Jail 的 IP，必须是​**唯一**​的。
* ② 指代先前创建的 bridge。

## 17.5.4. 创建 Linux Jail

FreeBSD 可以在 jail 内运行 Linux，利用 [Linux 二进制兼容性](https://docs.freebsd.org/en/books/handbook/linuxemu/#linuxemu) 和 [debootstrap(8)](https://man.freebsd.org/cgi/man.cgi?query=debootstrap\&sektion=8\&format=html)。jail 无内核，它们运行在宿主机的内核上。因此，需要在宿主系统中启用 Linux 二进制兼容性。

要在启动时启用 Linux ABI，执行以下命令：

```sh
# sysrc linux_enable="YES"
```

启用后，可以通过执行以下命令在不重启的情况下启动它：

```sh
# service linux start
```

接下来的步骤是按照上述方法创建 jail，例如在 [使用 OpenZFS 快照创建瘦 Jail](https://docs.freebsd.org/en/books/handbook/jails/#creating-thin-jail-openzfs-snapshots) 中所示，但 **不进行** 配置。FreeBSD Linux jails 需要特定的配置，接下来将详细说明。

待按照上述说明创建了 jail，执行以下命令进行所需配置并启动它：

```sh
# jail -cm \
    name=ubuntu \
    host.hostname="ubuntu.example.com" \
    path="/usr/local/jails/ubuntu" \
    interface="em0" \
    ip4.addr="192.168.1.150" \
    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
```

要访问 jail，首先需要安装 [sysutils/debootstrap](https://cgit.freebsd.org/ports/tree/sysutils/debootstrap/)。

执行以下命令访问 FreeBSD Linux jail：

```sh
# jexec -u root ubuntu
```

在 jail 内，执行以下命令安装 [sysutils/debootstrap](https://cgit.freebsd.org/ports/tree/sysutils/debootstrap/) 并准备 Ubuntu 环境：

```sh
# pkg install debootstrap
# debootstrap jammy /compat/ubuntu
```

当进程完成并在控制台上显示 `Base system installed successfully` 信息时，需要从宿主系统停止 jail，执行以下命令：

```sh
# service jail onestop ubuntu
```

然后，在 **/etc/jail.conf** 中添加 Linux jail 的条目：

```ini
ubuntu {
  # 启动/日志记录
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

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

  # 主机名/路径
  host.hostname = "${name}";
  path = "/usr/local/jails/containers/${name}";

  # 网络
  ip4.addr = 192.168.1.155;
  interface = em0;

  # 挂载
  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";
}
```

然后，使用以下命令像往常一样启动 jail：

```sh
# service jail start ubuntu
```

可以使用以下命令访问 Ubuntu 环境：

```sh
# jexec ubuntu chroot /compat/ubuntu /bin/bash
```

更多信息，请参阅 [Linux 二进制兼容性章节](https://docs.freebsd.org/en/books/handbook/linuxemu/#linuxemu)。

## 17.5.5. 配置服务 Jail

服务 jail 完全通过 **/etc/rc.conf** 或 [sysrc(8)](https://man.freebsd.org/cgi/man.cgi?query=sysrc\&sektion=8\&format=html) 配置。基本系统服务已经准备好作为服务 jail。它们包含一行配置，启用网络连接或解除其他 jail 限制。不适合在 jail 中运行的基本系统服务，即使在 **/etc/rc.conf** 中启用，也被配置为不作为服务 jail 启动。例如，某些服务会在启动或停止方法中挂载或卸载某些内容，或者仅配置诸如路由、防火墙等内容。

第三方服务可能已经准备好作为服务 jail，或者可能没有。要检查某个服务是否已经准备好作为服务 jail，可以使用以下命令：

```sh
# grep _svcj_options /path/to/rc.d/servicename
```

如果没有输出，表示该服务没有准备好作为服务 jail，或者不需要任何额外的权限（例如，网络访问）。

如果服务没有准备好作为服务 jail，并且需要网络访问，可以通过向 **/etc/rc.conf** 添加必要的配置来使其准备好：

```sh
# sysrc servicename_svcj_options=net_basic
```

有关所有可能的 `_svcj_options`，请参阅 [rc.conf(5)](https://man.freebsd.org/cgi/man.cgi?query=rc.conf\&sektion=5\&format=html) 手册页。

要为某个服务启用服务 jail，需要先停止该服务，然后将 `servicename_svcj` 变量设置为 YES。以将 [syslogd(8)](https://man.freebsd.org/cgi/man.cgi?query=syslogd\&sektion=8\&format=html) 放入服务 jail 为例，执行以下命令：

```sh
# service syslogd stop
# sysrc syslogd_svcj=YES
# service syslogd start
```

如果更改了 `servicename_svcj` 变量，必须在更改之前停止该服务。如果服务没有停止，rc 框架将无法检测到服务的正确状态，并且无法执行所请求的操作。

服务 jails 仅通过 [rc.conf(5)](https://man.freebsd.org/cgi/man.cgi?query=rc.conf\&sektion=5\&format=html)/[sysrc(8)](https://man.freebsd.org/cgi/man.cgi?query=sysrc\&sektion=8\&format=html) 和 [service(8)](https://man.freebsd.org/cgi/man.cgi?query=service\&sektion=8\&format=html) 命令进行管理。可以使用 jail 工具（如 [jls(8)](https://man.freebsd.org/cgi/man.cgi?query=jls\&sektion=8\&format=html)，在 [Jail 管理](https://docs.freebsd.org/en/books/handbook/jails/#jail-management) 中有描述）来检查服务的运行情况，但不建议使用 [jail(8)](https://man.freebsd.org/cgi/man.cgi?query=jail\&sektion=8\&format=html) 命令来管理它们。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://handbook.bsdcn.org/di-17-zhang-jail-yu-rong-qi/17.5.-shou-jail.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
