FreeBSD 使用手册
上一页 | 第16章 　Jails | 下一页

# 16.6 Jail 的应用

## 16.6.1 服务 Jail

原作 Daniel Gerzo.

这一节主要基于 Simon L. Nielsen `<simon@FreeBSD.org>` 的 http://simon.nitro.dk/service-jails.html 中的思路， 以及由 Ken Tom `<locals@gmail.com>` 更新的文档。 这一节中描述了如何配置 FreeBSD 系统的 jail(8) 功能为其增加一个安全层次。 这部分假定您运行 RELENG_6_0 或更新版本， 并理解本章之前部分的内容。

### 16.6.1.1 设计

jail 的一个主要问题是如何对它们进行升级和管理。 由于每个 jail 都是从头联编的， 对于单个 jail 而言升级也许还不是个很严重的问题， 因为升级不会太过麻烦， 而对于多个 jail 而言， 升级不仅会耗费大量时间， 并且是十分乏味的过程。

> 警告: 这个配置过程需要您对 FreeBSD 有较多的配置和使用经验。 如果这些过程显得太过复杂， 您应考虑使用较简单的系统， 例如 `sysutils/ezjail`， 它提供了更简单的管理 FreeBSD jail 的方法。

基本的想法是， 在不同的 jail 中尽可能多地以安全的方式使用共享的资源 ── 使用只读的 mount_nullfs(8) 挂接， 这会让升级简单许多， 从而使为每个服务建立不同的 jail 这种方案变得更加可行。 另外， 它也为增加、删除以及升级 jail 提供了更为便捷的方法。

> 注意: 在这里服务的常见例子包括： HTTP 服务、 DNS 服务、 SMTP 服务等等， 诸如此类。

这节介绍的配置的目的包括：

- 建立简单并易于理解的 jail 结构。 也就是说 不必 为每个 jail 执行完整的 installworld 操作。

- 使增删 jail 更容易。

- 使更新或升级 jail 更容易。

- 使运行自订的 FreeBSD 分支成为可能。

- 对安全的更偏执的追求， 尽可能减少被攻陷的可能。

- 尽可能节省空间和 inode。

如前面提到的那样， 这个设计极大程度上依赖于将一份只读的主模板 (known as nullfs) 挂接到每一个 jail 中， 并为每个 jail 配置一个可读写的设备。 这种设备可以是物理磁盘、 分区， 或以 vnode 为后端的 md(4) 设备。 在这个例子中， 我们将使用可读写的 nullfs 挂接。

下面的表中描述了文件系统格局：

- 每个 jail 挂接到 `/home/j` 目录下的一个目录。

- `/home/j/mroot` 是每个 jail 共用的模板， 对于所有的 jail 而言都是只读的。

- 在 `/home/j` 目录中， 每个 jail 有一个对应的空目录。

- 每个 jail 中都有一个 `/s` 目录， 这个目录将连接到系统中的可读写部分。

- 每个 jail 应基于 `/home/j/skel` 建立其可读写空间。

- 每个 jailspace (jail 中的可读写部分) 应创建到 `/home/js`。

> 注意: 这假定所有的 jail 都放置于 `/home` 分区中。 当然， 您可以根据需要将这个配置改为需要的任何样子， 但在接下来的例子中， 也应相应地加以变动。

### 16.6.1.2 建立模板

这一节将介绍创建 jail 所需的只读主模板所需的步骤。

一般来说， 您应将系统升级到最新的 FreeBSD -RELEASE 分支， 具体做法请参见本手册的相关 章节。 当更新不可行时， 则需要完成 buildworld 过程， 另外， 您还需要 `sysutils/cpdup` 软件包。 我们将使用 portsnap(8) 工具来下载 FreeBSD Ports 套件。 在使用手册的 Portsnap 章节 中， 提供了针对初学者的介绍。

1. 首先， 需要为将要存放只读的 FreeBSD 执行文件的文件系统建立一个目录， 接着进入 FreeBSD 源代码的目录， 并在其中安装 jail 模板：

       # mkdir /home/j /home/j/mroot
       # cd /usr/src
       # make installworld DESTDIR=/home/j/mroot

2. 接着， 准备一份 FreeBSD Ports 套件， 以及用于执行 mergemaster 的 FreeBSD 源代码：

       # cd /home/j/mroot
       # mkdir usr/ports
       # portsnap -p /home/j/mroot/usr/ports fetch extract
       # cpdup /usr/src /home/j/mroot/usr/src

3. 创建系统中可读写部分的骨架：

       # mkdir /home/j/skel /home/j/skel/home /home/j/skel/usr-X11R6 /home/j/skel/distfiles
       # mv etc /home/j/skel
       # mv usr/local /home/j/skel/usr-local
       # mv tmp /home/j/skel
       # mv var /home/j/skel
       # mv root /home/j/skel

4. 使用 mergemaster 安装缺失的配置文件。 接下来， 删除 mergemaster 创建的多余目录：

       # mergemaster -t /home/j/skel/var/tmp/temproot -D /home/j/skel -i
       # cd /home/j/skel
       # rm -R bin boot lib libexec mnt proc rescue sbin sys usr dev

5. 现在， 将可读写文件系统连接到只读文件系统中。 请确保您在 `s/` 目录中建立了适当的符号连接。 如果没有建立目录或建立的位置不正确， 可能会导致安装失败。

       # cd /home/j/mroot
       # mkdir s
       # ln -s s/etc etc
       # ln -s s/home home
       # ln -s s/root root
       # ln -s ../s/usr-local usr/local
       # ln -s ../s/usr-X11R6 usr/X11R6
       # ln -s ../../s/distfiles usr/ports/distfiles
       # ln -s s/tmp tmp
       # ln -s s/var var

6. 最后， 创建一个默认的包含下列配置的 `/home/j/skel/etc/make.conf`：

       WRKDIRPREFIX?=  /s/portbuild

   配置 `WRKDIRPREFIX` 使得在每个 jail 中分别编译 FreeBSD 成为可能。 请注意 ports 目录是只读系统的一部分。 而自订的 `WRKDIRPREFIX` 则使得联编过程得以在 jail 中的可读写部分完成。

### 16.6.1.3 建立 Jail

现在我们已经有了完整的 FreeBSD jail 模板， 可以在 `/etc/rc.conf` 中安装并配置它们了。 这个例子中演示了建立 3 个 jail： “NS”、 “MAIL” 和 “WWW”。

1. 在 `/etc/fstab` 文件中加入下列配置， 以便让系统自动挂接 jail 的只读模板和读写空间：

       /home/j/mroot   /home/j/ns     nullfs  ro  0   0
       /home/j/mroot   /home/j/mail   nullfs  ro  0   0
       /home/j/mroot   /home/j/www    nullfs  ro  0   0
       /home/js/ns     /home/j/ns/s   nullfs  rw  0   0
       /home/js/mail   /home/j/mail/s nullfs  rw  0   0
       /home/js/www    /home/j/www/s  nullfs  rw  0   0

   > 注意: 扫描批次号 (pass number) 为 0 的分区不会在启动时使用 fsck(8) 进行检查， 而转存批次号 (dump number) 为 0 的分区则不会在 dump(8) 时备份。 我们不希望 fsck 检查 nullfs 挂接， 或让 dump 备份 jail 中的只读 nullfs 挂接。 这就是为什么在每个 `fstab` 条目的最后两列是 “0 0” 的原因。

2. 在 `/etc/rc.conf` 中配置 jail：

       jail_enable="YES"
       jail_set_hostname_allow="NO"
       jail_list="ns mail www"
       jail_ns_hostname="ns.example.org"
       jail_ns_ip="192.168.3.17"
       jail_ns_rootdir="/usr/home/j/ns"
       jail_ns_devfs_enable="YES"
       jail_mail_hostname="mail.example.org"
       jail_mail_ip="192.168.3.18"
       jail_mail_rootdir="/usr/home/j/mail"
       jail_mail_devfs_enable="YES"
       jail_www_hostname="www.example.org"
       jail_www_ip="62.123.43.14"
       jail_www_rootdir="/usr/home/j/www"
       jail_www_devfs_enable="YES"

   > 警告: 应把 `jail_`name`_rootdir` 变量设置成 `/usr/home` 而不是 `/home` 的原因是 `/home` 目录在默认安装的 FreeBSD 上是指向 `/usr/home` 的一个符号连接。 而 `jail_`name`_rootdir` 变量必须是一个 不 包含符号连接的路径， 否则 jail 将拒绝启动。 可以使用 realpath(1) 工具来决定这一变量应被赋予一个什么样的值。 更详细的信息请参阅安全公告 FreeBSD-SA-07:01.jail

3. 为每个 jail 创建所需的只读文件系统挂接点：

       # mkdir /home/j/ns /home/j/mail /home/j/www

4. 在 jail 中安装可读写的模板。 注意您需要使用 `sysutils/cpdup`， 它能够帮助您确保每个目录都是正确地复制的：

       # mkdir /home/js
       # cpdup /home/j/skel /home/js/ns
       # cpdup /home/j/skel /home/js/mail
       # cpdup /home/j/skel /home/js/www

5. 这样， 就完成了 jail 的制作， 可以运行了。 首先为 jail 挂接文件系统， 然后使用 `/etc/rc.d/jail` 脚本来启动它们：

       # mount -a
       # /etc/rc.d/jail start

现在 jail 应该就启动起来了。 要检查它们是否运行正常， 可以使用 jls(8) 命令。 它的输出应该类似这样：

    # jls
       JID  IP Address      Hostname                      Path
         3  192.168.3.17    ns.example.org                /home/j/ns
         2  192.168.3.18    mail.example.org              /home/j/mail
         1  62.123.43.14    www.example.org               /home/j/www

这时， 就可以登入 jail 并增加用户和配置服务了。 `JID` 列给出了正在运行的 jail 的标识编号。 您可以使用下面的命令来在 `JID` 编号为 3 的 jail 中执行管理任务：

    # jexec 3 tcsh

### 16.6.1.4 升级

有时， 由于安全问题， 或新增功能有用， 会希望将系统升级到一个新版本的 FreeBSD。 这种安装方式的设计使得升级现有 jail 变得很容易。 另外， 它也能最大限度地减小停机时间， 因为 jail 只在最后时刻才需要关闭。 另外， 它也提供了简单的回退到先前版本的方法。

1. 第一步是按通常的方法升级主机的系统。 接着， 在 `/home/j/mroot2` 中建立一个新的临时模板：

       # mkdir /home/j/mroot2
       # cd /usr/src
       # make installworld DESTDIR=/home/j/mroot2
       # cd /home/j/mroot2
       # cpdup /usr/src usr/src
       # mkdir s

   在运行 `installworld` 时会创建一些不需要的目录， 应将它们删除：

       # chflags -R 0 var
       # rm -R etc var root usr/local tmp

2. 重建到主系统中的可读写符号连接：

       # ln -s s/etc etc
       # ln -s s/root root
       # ln -s s/home home
       # ln -s ../s/usr-local usr/local
       # ln -s ../s/usr-X11R6 usr/X11R6
       # ln -s s/tmp tmp
       # ln -s s/var var

3. 现在是时候关闭 jail 了：

       # /etc/rc.d/jail stop

4. 卸下原先的文件系统：

       # umount /home/j/ns/s
       # umount /home/j/ns
       # umount /home/j/mail/s
       # umount /home/j/mail
       # umount /home/j/www/s
       # umount /home/j/www

   > 注意: 可读写的文件系统 (`/s`) 会在只读系统之后挂接， 因此应首先卸载。

5. 将先前的只读文件系统挪走， 换成新的系统。 这样做也同时保留了先前系统的备份， 从而可以在出现问题时从中恢复。 这里我们根据新系统的创建时间来命名。 此外我们把先前的 FreeBSD Ports 套件直接移动到新的文件系统中， 以节省磁盘空间和 inode：

       # cd /home/j
       # mv mroot mroot.20060601
       # mv mroot2 mroot
       # mv mroot.20060601/usr/ports mroot/usr

6. 现在新的只读模板就可以用了， 剩下的事情是重新挂接文件系统并启动 jails：

       # mount -a
       # /etc/rc.d/jail start

最后用 jls(8) 检查 jail 启动是否正常。 不要忘记在 jail 中运行 mergemaster。 配置文件和 rc.d 脚本在升级时应进行更新。

上一页 | 起始页 | 下一页
微调和管理 | 上一级 | 强制访问控制

本文档和其它文档可从这里下载：ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题，请先阅读文档，如不能解决再联系<questions@FreeBSD.org>.
关于本文档的问题请发信联系 <doc@FreeBSD.org>.