本節主要以 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) 掛載,來讓升級更簡單, 並把各個 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 分割區。 當然, 也可以依自身需求更改,但接下來的例子中, 也要記得修改相對應的地方。
本節將逐步介紹如何建立 jail 要用的唯讀主模版。
建議先把 FreeBSD 系統升級到最新的 -RELEASE 分支,至於如何做請參閱 Handbook 的 相關章節。 當更新完成之後,就要進行 buildworld 程序,此外還要裝 sysutils/cpdup 套件。 我們將用 portsnap(8) 來下載 FreeBSD Ports Collection, 在 Handbook 中對 Portsnap 章節 中有相關介紹,初學者可以看看。
首先,先建立唯讀的目錄結構給 jail 放 FreeBSD binary, 接著到 FreeBSD source tree 目錄,並安裝 jail 模版:
# mkdir -p /home/j/mroot # cd /usr/src # make installworld DESTDIR=/home/j/mroot
接著跟 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
建立可讀寫部分的骨架:
# 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
現在把可讀寫的檔案系統以 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
最後則是新增 /home/j/skel/etc/make.conf ,並填入以下內容:
WRKDIRPREFIX?= /s/portbuild
要設定 WRKDIRPREFIX 才可以讓各 jail 得以順利編譯 FreeBSD ports。請記住 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
從 /home 改為 /usr/home 的原因在於 FreeBSD 預設安裝的 /home 目錄其實只是指向 /usr/home 的 symbolic link。 而jail_name_rootdir
變數須為 實體目錄 而非 symbolic link, 否則 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 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
有時由於安全問題或者 jail 內要用新功能,而需要把 FreeBSD 系統升級到更新。 這種安裝設計方式讓既有的 jail 升級變得更加容易。 jail 也可以把 service 停機時間(downtime)降到最低,因為 jail 只需在最後關鍵才需要重開。 此外,萬一新版有問題的話, 它也提供輕鬆回溯到舊版的功能。
首先是照一般方式來升級 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
重新建立到主系統的可讀寫空間 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
現在可以關閉 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 Collection 直接搬到新的檔案系統,以節省硬碟空間與 inode :
# cd /home/j # mv mroot mroot.20060601 # mv mroot2 mroot # mv mroot.20060601/usr/ports mroot/usr
現在新的唯讀模版準備好了,只剩下重新掛載以及啟動 jail:
# mount -a # /etc/rc.d/jail start
最後以 jls(8) 來檢查 jail 是否均正常啟動。 別忘了要在各 jail 內執行 mergemaster,還有相關設定檔以及 rc.d scripts 均要更新。
本文及其他文件,可由此下載:ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/。
若有 FreeBSD 方面疑問,請先閱讀 FreeBSD 相關文件,如不能解決的話,再洽詢
<questions@FreeBSD.org>。
關於本文件的問題,請洽詢 <doc@FreeBSD.org>。