本節主要以 Simon L. B. Nielsen 寫的 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_
變數須為 實體目錄 而非 symbolic link,
否則 jail 會拒絕啟動。 可以用 realpath(1)
來決定該變數。 詳情請參閱 FreeBSD-SA-07:01.jail 安全通告。name
_rootdir
替每個 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 均要更新。
All FreeBSD documents are available for download at http://ftp.FreeBSD.org/pub/FreeBSD/doc/
Questions that are not answered by the
documentation may be
sent to <freebsd-questions@FreeBSD.org>.
Send questions about this document to <freebsd-doc@FreeBSD.org>.