For the complete documentation index, see llms.txt. This page is also available as Markdown.

17.3.主机配置

在宿主系统上创建任何 jail 之前,必须做一些配置,并获取宿主系统的一些信息。

这包括配置 jail(8) 工具、创建用于配置和安装 jail 的必要目录、获取宿主系统的网络信息,以及检查宿主是否使用 OpenZFS 或 UFS 作为文件系统。

技巧

jail 中运行的 FreeBSD 版本不得高于宿主系统运行的版本。

17.3.1. jail 工具

jail(8) 工具用于管理 jail。

若要在系统启动时自动启动 jail,执行以下命令:

# sysrc jail_enable="YES"
# sysrc jail_parallel_start="YES"

技巧

启用 jail_parallel_start 后,所有已配置的 jail 将在后台同时启动。

默认情况下,配置文件中有条目的每个 jail 都会在系统启动时启动。如果只想启动选定的 jail,可在 jail_list 中列出它们的名称:

# sysrc jail_list="www db"

jail_list 中的名称以空格分隔,必须与 /etc/jail.conf/etc/jail.conf.d/ 中定义的 jail 名称匹配。只有列表中的 jail 会在启动时启动,并按给定顺序启动,因此未在 jail_list 中的 jail 必须手动启动。当 jail_list 为空时,配置文件中定义的所有 jail 都会启动。

要在关机时按 jail_list 的相反顺序停止 jail,可设置 jail_reverse_stop

# sysrc jail_reverse_stop="YES"

注意

启用 jail_parallel_start 后,jail 在后台启动,jail_list 的顺序不再得到保证。当某个 jail 必须在另一个 jail 启动之前运行时,请在 /etc/jail.conf 中使用 depend 参数来表达这种依赖关系,而不是依赖列表顺序。jail 的依赖会在其启动之前自动创建,并在其停止时以相反顺序停止,这与 jail_list 无关。depend 参数将在 jail 配置文件 一节中介绍。

17.3.2. 网络

FreeBSD jail 的网络可通过几种不同的方式配置:

宿主网络模式(IP 共享) 在宿主网络模式中,jail 共享宿主的网络协议栈,而不是获得自己的协议栈。共享协议栈并不意味着共享地址:常见做法是为 jail 分配独立的 ip4.addr(和 ip6.addr),由 jail(8) 作为别名添加到宿主接口上,因此 jail 和宿主在同一协议栈上通过不同地址应答。只有配置为 ip4 = inherit 的 jail 才会完整共享宿主的地址,应答宿主自己的地址,并无限制地访问宿主持有的所有地址。

虚拟网络(VNET) 虚拟网络是 FreeBSD jail 提供的高级网络功能,比基础的宿主网络模式更灵活。VNET 能为每个 jail 创建独立的网络协议栈,为其分配独立的 IP 地址、路由表和网络接口。这提供了更高程度的网络隔离,使 jail 的运行行为更像是独立的虚拟机。

netgraph 系统 netgraph(4) 是通用的内核框架,用于创建自定义网络配置。它可用于定义 jail 与宿主之间、以及多个 jail 之间网络流量的流向方式。

17.3.2.1. jail 的 IP 地址来源

共享协议栈的 jail 通过宿主接口上的地址访问网络。interface 参数指定该接口,对于 ip4.addrip6.addr 中的每个地址,jail(8) 都会自行管理别名:正如 jail(8) 所述,“在创建 jail 之前,会为每个地址在接口上添加一个别名,并在移除 jail 之后从接口上移除该别名”。宿主上无需预先配置该地址。

也可以按地址指定接口,从而用一行将地址与承载它的接口绑定:

当地址必须独立于 jail 持久存在时——因为宿主上的另一个服务依赖它,或者因为未使用 interface 参数——可在宿主上将其配置为 rc.conf 别名,不交给 jail(8) 管理:

较早的编号形式 ifconfig_em0_alias0="inet 192.168.1.151/32" 仍然可用,但对于新配置,推荐使用 _aliases 形式,因为它避免了编号别名必须形成连续 alias0alias1alias2 序列这种脆弱的要求。

17.3.2.2. 共享 IP jail 中的回环地址

共享协议栈的 jail 没有自己的回环接口。FreeBSD 内核对此做了补偿:在非 VNET jail 中,只要 127.0.0.1::1 本身不是 jail 自己的地址之一,内核就会将回环地址上的 bind()connect() 静默改写为 jail 的第一个 ip4.addrip6.addr。此行为未在 jail(8) 中记录,但它很重要,因为它带有安全隐患。

警告

不要以为将服务绑定到 localhost 就能将其限制在 jail 内部。在共享 IP jail 中,被告知监听 127.0.0.1 的守护进程实际上绑定的是 jail 的第一个地址,这通常是面向局域网的地址,因此可从网络访问。

标准的缓解方法是给 jail 分配一个私有的回环地址并将其列在首位。在宿主上克隆一个专用的回环接口,并为其分配 127.0.0.0/8 范围内的地址(或私有 RFC 1918 地址):

然后将该地址列在 jail 的 ip4.addr 中的首位,位于任何可路由地址之前,这样回环改写就会落到私有地址而非面向局域网的地址上。

17.3.2.3. 准备宿主服务

每个 jail 别名地址都共享宿主的端口。因此,绑定到通配符地址(在监听器列表中显示为 *:port)的宿主守护进程也会监听每个 jail 别名,并在 jail 自己的服务可以占用该端口之前就将其占用。在启动任何 jail 之前,先审计宿主已监听的内容:

限制每个绑定通配符地址的守护进程,使其仅使用宿主自己的地址,将 jail 地址留空:

  • syslogd(8):在 /etc/rc.conf 中设置 syslogd_flags="-ss"。单个 -s 已经使 syslogd 以安全模式运行,不监听远程日志消息;根据 syslogd(8),如果指定两次 -s,“将完全不打开网络套接字”,这同时也会阻止 syslogd 向远程主机发送消息。

  • sshd(8):在 /etc/ssh/sshd_config 中将 ListenAddress 固定到宿主的地址,而不是保留默认的通配符。

  • ntpd(8)sendmail(8):通过它们各自的配置将它们绑定到宿主地址。

关于哪些地址可由 jail 使用的防火墙级控制,请参见 防火墙 一章。

17.3.3. 设置 jail 目录结构

jail 的文件不需要放在特定路径。

有些管理员使用 /jail,有些使用 /usr/jail,也有些使用 /usr/local/jails。本章将使用 /usr/local/jails

除了 /usr/local/jails,还将创建以下目录:

  • media:用于存放下载的用户空间压缩包。

  • templates:使用瘦 jail 时用于存放模板。

  • containers:用于存放各个 jail。

如果使用 OpenZFS,执行以下命令为这些目录创建数据集:

技巧

此处使用 zroot 作为父数据集,也可使用其他数据集。

为每个 jail 创建单独的子数据集,是实现每个 jail 独立快照、克隆、配额和数据集委托的基础。由于每个 jail 目录都是独立的数据集,OpenZFS 可以使用 quotareservation 属性为其磁盘使用设置上限和保证;参见 jail 资源限制

如果使用 UFS,执行以下命令创建这些目录:

17.3.4. jail 配置文件

配置 jail 有两种方式。

第一种是在 /etc/jail.conf 文件中为每个 jail 添加条目。另一种方式是在 /etc/jail.conf.d/ 目录中为每个 jail 创建独立的配置文件。

如果宿主系统上只运行少量 jail,可直接将每个 jail 的配置写入 /etc/jail.conf 文件。若宿主系统上运行多个 jail,建议在 /etc/jail.conf.d/ 目录下为每个 jail 分别创建配置文件。

/etc/jail.conf.d/ 中的文件必须使用 .conf 作为扩展名,并且需要在 /etc/jail.conf 中通过以下语句引入:

典型的 jail 配置条目如下:

  • jailname jail 的名称。

  • exec.start 在 jail 创建时运行的命令。常见写法为 /bin/sh /etc/rc

  • exec.stop 在移除 jail 前运行的命令。常见写法为 /bin/sh /etc/rc.shutdown

  • exec.consolelog 用于记录命令输出(stdout 和 stderr)的日志文件路径。

  • allow.raw_sockets 能在 jail 中创建原始套接字。启用后,可在 jail 中使用如 ping(8)traceroute(8) 等工具。

  • exec.clean 在干净的环境中运行命令。

  • mount.devfsdevfs(5) 文件系统挂载到 chroot 后的 /dev 目录,以限制 jail 内部可见的设备。它应用 devfs_ruleset 参数指定的规则集,或者当该参数保留为默认值 0 时,使用 mount.devfs 的回退规则集 4,即 devfsrules_jail,该规则集隐藏了除最小化 jail 安全设备集之外的所有宿主设备(参见 devfs 规则集)。

  • host.hostname jail 的主机名。

  • path jail 的根目录路径。通过 jailjexec(8) 执行的命令均在该目录下运行。

  • ip4.addr 分配给 jail 的一个 IPv4 地址,或以逗号分隔的地址列表。设置 ip4.addr 隐含了 ip4 = newip4 本身不会保留为独立的默认值。ip4 参数接受三个值:new 将 jail 限制为 ip4.addr 中列出的地址,inherit 让 jail 无限制地访问宿主的所有 IPv4 地址,disable 完全关闭 jail 中的 IPv4。未设置 ip4ip4.addr 的 jail 行为类似于 inherit,此时 interfaceip4.addrinherit 下无效。在有多个地址的 jail 中,ip4.saddrsel 让内核为未绑定套接字的出站连接选择源地址,而 ip4.nosaddrsel 强制使用列表中的第一个地址。

  • ip6.addr 分配给 jail 的一个 IPv6 地址,或以逗号分隔的地址列表。ip6 参数接受与 ip4 相同的 newinheritdisable 值,ip6.saddrselip6.nosaddrsel 控制源地址选择。对于仅 IPv4 的 jail,使用 ip6 = disable;对于仅 IPv6 的 jail,使用 ip4 = disable

  • interface 用于添加 jail IP 地址的网络接口,通常为宿主系统的接口名。

注意

jail 没有虚拟控制台:init(8)getty(8) 不会在其中运行,因此没有可交互连接的对象。exec.consolelog 文件仅捕获 exec.* 命令的标准输出和标准错误,即 /etc/rc 启动脚本和 /etc/rc.shutdown 脚本的输出。它不是交互式控制台;如需交互,可使用 jexec(8)(参见 访问 jail)或在 jail 内运行 sshd(8)。jail 自己的守护进程会将后续输出发送到 jail 的 syslog。

技巧

exec.consolelog 文件会无限增长。可通过 newsyslog(8) 添加单个 glob 条目来轮转,例如在 /etc/newsyslog.conf.d/jail 中:

17.3.4.1. 全局设置、通配符和变量

在任何 jail 块之外设置的参数是通配符默认值:jail.conf(5) 会将它们应用于每个 jail,就像写在 * 通配符节中一样。一次性设置共享参数可使每个 jail 块保持简短。

以美元符号开头、可选地用大括号括起的名称,会被替换为该参数或变量的值。内置的 ${name} 会展开为 jail 自己的名称,自定义变量的定义方式与参数相同,但以美元符号开头。

以下 /etc/jail.conf 一次性定义了公共头信息,并仅覆盖每个 jail 不同的部分:

此处 www 继承了头信息中的所有内容,仅设置了自己的地址,而 db 还覆盖了 host.hostname

17.3.4.2. 命令钩子

jail(8) 通过一系列生命周期钩子来运行 jail,每个钩子是在宿主环境还是在 jail 内部运行都很重要。创建顺序为:

exec.prestart 在创建 jail 之前于宿主环境运行。 exec.created 在 jail 已存在但尚未启动任何进程之前于宿主环境运行。 exec.start 在 jail 内部运行,通常为 /bin/sh /etc/rc 以启动其服务。 exec.poststart 在 jail 启动之后于宿主环境运行。

关闭时运行的是镜像过程:

exec.prestop 在停止 jail 之前于宿主环境运行。 exec.stop 在 jail 内部运行,通常为 /bin/sh /etc/rc.shutdownexec.poststop 在 jail 停止之后于宿主环境运行,用于拆除 exec.prestart 设置的内容。

可通过 += 为一个钩子分配多个命令:

exec.timeout 限制了 jail(8) 等待命令完成的时间,exec.consolelog 捕获这些命令的输出。VNET jail 一节使用 exec.prestartexec.poststop 在宿主上构建和拆除每个 jail 的网络管道。

钩子在单个 jail 内部为命令排序,而 depend 参数则为整个 jail 排序。写入 depend = otherjail; 可告知 jail(8) 该 jail 需要 otherjail:依赖项会在此 jail 创建之前首先创建,直到其最后一个 exec.poststart 命令完成,并且这些 jail 会以相反顺序拆除。

17.3.4.3. devfs 规则集

每个设置 mount.devfs 的 jail 都会获得一个 devfs 规则集,用于决定其 /dev 下出现哪些设备节点,该规则集要么是 devfs_ruleset 参数指定的,要么在该参数未设置时使用 mount.devfs 的默认规则集 4。devfs.rules(5)/etc/defaults/devfs.rules 中定义命名规则集。其中两个面向 jail:devfsrules_jail(规则集 4)是标准的 jail 最小设备集,devfsrules_jail_vnet(规则集 5)额外暴露 /dev/pf,使 VNET jail 可以运行自己的防火墙。VNET jail 示例设置 devfs_ruleset = 5,Linux jail 示例设置 devfs_ruleset = 4;传统 jail 和瘦 jail 示例仅设置 mount.devfs,因此使用其默认的规则集 4。

要暴露额外设备,可在 /etc/devfs.rules 中编写自定义规则集,包含一个标准规则集并取消隐藏所需设备。以下规则集从 devfsrules_jail 开始,并额外取消隐藏 bpf(4) 设备:

重新加载规则,然后在 jail 配置中按编号引用新规则集:

/etc/devfs.rules 中的规则集会覆盖 /etc/defaults/devfs.rules 中同号的规则集;否则这两个文件将有效合并。

关于配置变量的更多信息,可参考 jail(8)jail.conf(5)

最后更新于