这一节主要基于 Simon L. Nielsen <simon@FreeBSD.org>
的 http://simon.nitro.dk/service-jails.html 中的思路, 以及由 Ken Tom
<locals@gmail.com>
更新的文档。
这一节中描述了如何配置 FreeBSD 系统的 jail(8)
功能为其增加一个安全层次。 这部分假定您运行 RELENG_6_0 或更新版本,
并理解本章之前部分的内容。
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 分区中。 当然, 您可以根据需要将这个配置改为需要的任何样子, 但在接下来的例子中, 也应相应地加以变动。
这一节将介绍创建 jail 所需的只读主模板所需的步骤。
一般来说, 您应将系统升级到最新的 FreeBSD -RELEASE 分支, 具体做法请参见本手册的相关 章节。 当更新不可行时, 则需要完成 buildworld 过程, 另外, 您还需要 sysutils/cpdup 软件包。 我们将使用 portsnap(8) 工具来下载 FreeBSD Ports 套件。 在使用手册的 Portsnap 章节 中, 提供了针对初学者的介绍。
首先, 需要为将要存放只读的 FreeBSD 执行文件的文件系统建立一个目录, 接着进入 FreeBSD 源代码的目录, 并在其中安装 jail 模板:
# mkdir /home/j /home/j/mroot # cd /usr/src # make installworld DESTDIR=/home/j/mroot
接着, 准备一份 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
创建系统中可读写部分的骨架:
# 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
使用 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
现在, 将可读写文件系统连接到只读文件系统中。 请确保您在 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
最后, 创建一个默认的包含下列配置的 /home/j/skel/etc/make.conf:
WRKDIRPREFIX?= /s/portbuild
配置 WRKDIRPREFIX 使得在每个 jail 中分别编译 FreeBSD 成为可能。 请注意 ports 目录是只读系统的一部分。 而自订的 WRKDIRPREFIX 则使得联编过程得以在 jail 中的可读写部分完成。
现在我们已经有了完整的 FreeBSD jail 模板, 可以在 /etc/rc.conf 中安装并配置它们了。 这个例子中演示了建立 3 个 jail: “NS”、 “MAIL” 和 “WWW”。
在 /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
在 /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
为每个 jail 创建所需的只读文件系统挂接点:
# mkdir /home/j/ns /home/j/mail /home/j/www
在 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
这样, 就完成了 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
有时, 由于安全问题, 或新增功能有用, 会希望将系统升级到一个新版本的 FreeBSD。 这种安装方式的设计使得升级现有 jail 变得很容易。 另外, 它也能最大限度地减小停机时间, 因为 jail 只在最后时刻才需要关闭。 另外, 它也提供了简单的回退到先前版本的方法。
第一步是按通常的方法升级主机的系统。 接着, 在 /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
重建到主系统中的可读写符号连接:
# 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
现在是时候关闭 jail 了:
# /etc/rc.d/jail stop
卸下原先的文件系统:
# 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) 会在只读系统之后挂接, 因此应首先卸载。
将先前的只读文件系统挪走, 换成新的系统。 这样做也同时保留了先前系统的备份, 从而可以在出现问题时从中恢复。 这里我们根据新系统的创建时间来命名。 此外我们把先前的 FreeBSD Ports 套件直接移动到新的文件系统中, 以节省磁盘空间和 inode:
# cd /home/j # mv mroot mroot.20060601 # mv mroot2 mroot # mv mroot.20060601/usr/ports mroot/usr
现在新的只读模板就可以用了, 剩下的事情是重新挂接文件系统并启动 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>.