15.6. Jail 的應用
前一頁 | 章 15. Jails | 下一頁

## 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 而言，升級不僅會耗費太多時間，並相當枯燥乏味。

### 警告:

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

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

### 注意:

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`>。

### 注意:

這邊假設所有 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

   ### 注意:

   分割區的 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` 從 `/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`

   ### 注意:

   可讀寫空間(`/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 均要更新。

前一頁 | 上一層 | 下一頁
15.5. 微調與管理 | 起始頁 | 章 16. Mandatory Access Control

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

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