FreeBSD 使用手冊
Prev | Chapter 15 Jails | Next

# 15.6 Jail 的應用

## 15.6.1 Service Jails

Contributed by 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 或更新的版本， 並且對本章先前部分均能理解。

### 15.6.1.1 Design

Jail 的主要問題之一在於如何對其進行更新、升級和管理。 由於每個 jail 都是從頭重新編譯，對於單一 jail 而言， 升級也許還不是很嚴重的問題，因為更新、升級並不會太麻煩。 但對於一堆 jail 而言，升級不僅會耗費太多時間，並相當枯燥乏味。

> Warning: 這些設定的前提是您對 FreeBSD 使用、功能運用上有相當的經驗， 若下面的設定對您來說太過複雜，建議您該考慮用較簡易的系統，像是 `sysutils/ezjail`，其提供更簡單的 FreeBSD jail 管理方式。

基本的想法是在不同的 jail 中儘量以安全的方式來共用資源 —— 採用唯讀的 mount_nullfs(8) 掛載，來讓升級更簡單， 並把各個 service 放到不同的 jail 的作法會更加可行。 此外， 其也提供對於如何增加、刪除、升級 jail 的簡便方式。

> Note: service 常見的例子包括： HTTP server、DNS server、SMTP server 等等。

本節介紹的設定目的在於：

- 建立簡易且容易理解的 jail 架構。 也就是說 不必為每個 jail 都執行完整的 installworld 。

- 讓 jail 的新增、移除更簡單。

- 讓 jail 的更新、升級更輕鬆。

- 可以跑自行打造的 FreeBSD 分支。

- 對安全有更偏執狂的追求，儘可能降低被攻陷的可能。

- 儘量節省空間與 inode。

如同先前所提到的，這設計主要是靠把唯讀的主要模版 (也就是大家所熟知的 nullfs)掛載到每個 jail，並且讓每個 jail 有個可讀、寫的設備，這設備可以是獨立實體硬碟、 、分割區、或以 vnode 為後端的 md(4) 設備。 在本例當中， 我們採用可讀寫的 nullfs 掛載。

下面的表則介紹檔案系統的配置：

- 每個 jail 都會掛載到 `/home/j` 底下的其中一個目錄。

- `/home/j/mroot` 則是每個 jail 共用的模版，並對於所有 jail 而言都是唯讀。

- 每個 jail 在 `/home/j` 底下都有一個相對應的空目錄。

- 每個 jail 都會有 `/s` 目錄， 該目錄會連到系統的可讀寫部分。

- 每個 jail 都會在 `/home/j/skel` 目錄建立自屬的可讀寫空間 。

- 每個 jailspace (各 jail 可讀寫的部分) 都建在 `/home/js`>。

> Note: 這邊假設所有 jail 都放在 `/home` 分割區。 當然， 也可以依自身需求更改，但接下來的例子中， 也要記得修改相對應的地方。

### 15.6.1.2 建立模版

本節將逐步介紹如何建立 jail 要用的唯讀主模版。

建議先把 FreeBSD 系統升級到最新的 -RELEASE 分支，至於如何做請參閱 Handbook 的 相關章節。 當更新完成之後，就要進行 buildworld 程序，此外還要裝 `sysutils/cpdup` 套件。 我們將用 portsnap(8) 來下載 FreeBSD Ports Collection， 在 Handbook 中對 Portsnap 章節 中有相關介紹，初學者可以看看。

1. 首先，先建立唯讀的目錄結構給 jail 放 FreeBSD binary， 接著到 FreeBSD source tree 目錄，並安裝 jail 模版：

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

2. 接著跟 FreeBSD source tree 一樣，也把 FreeBSD Ports Collection 放一份供 jail 使用，以備 mergemaster ：

       # 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. 現在把可讀寫的檔案系統以 symlink 方式連到唯讀的檔案系統。 請確認 symbolic link 是否有正確連到 `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。請記住 ports 目錄是屬唯讀檔案系統。 而搭配自訂的 `WRKDIRPREFIX` 才可以讓各 jail 在可讀寫空間進行編譯。

### 15.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

   > Note: 分割區的 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"

   > Warning: 之所以要把 `jail_`name`_rootdir` 從 `/home` 改為 `/usr/home` 的原因在於 FreeBSD 預設安裝的 `/home` 目錄其實只是指向 `/usr/home` 的 symbolic link。 而 `jail_`name`_rootdir` 變數須為 實體目錄 而非 symbolic link， 否則 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` script 來啟動：

       # 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 並新增帳號與設定相關 service 要用的 daemon 。 上面的 `JID` 欄代表正在運作中的 jail 編號。 可用下列指令以在 `JID` 編號 3 的 jail 執行管理工作：

    # jexec 3 tcsh

### 15.6.1.4 升級

有時由於安全問題或者 jail 內要用新功能，而需要把 FreeBSD 系統升級到更新。 這種安裝設計方式讓既有的 jail 升級變得更加容易。 jail 也可以把 service 停機時間(downtime)降到最低，因為 jail 只需在最後關鍵才需要重開。 此外，萬一新版有問題的話， 它也提供輕鬆回溯到舊版的功能。

1. 首先是照一般方式來升級 host system，再新增臨時的唯讀模版 `/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. 重新建立到主系統的可讀寫空間 symlink：

       # 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

   > Note: 可讀寫空間(`/s`) 是掛載在唯讀檔案系統底下，故要先卸載。

5. 把舊的唯讀系統搬走，換成新的。 如此一來， 可同時保留先前系統的備份，以備萬一升級後有問題可回復。 這邊的命名方式採新唯讀檔案系統的建立時間，此外原先 FreeBSD Ports Collection 直接搬到新的檔案系統，以節省硬碟空間與 inode ：

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

6. 現在新的唯讀模版準備好了，只剩下重新掛載以及啟動 jail：

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

最後以 jls(8) 來檢查 jail 是否均正常啟動。 別忘了要在各 jail 內執行 mergemaster，還有相關設定檔以及 rc.d scripts 均要更新。

Prev | Home | Next
微調與管理 | Up | Mandatory Access Control

本文及其他文件，可由此下載：ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/。

若有 FreeBSD 方面疑問，請先閱讀 FreeBSD 相關文件，如不能解決的話，再洽詢 <questions@FreeBSD.org>。
關於本文件的問題，請洽詢 <doc@FreeBSD.org>。