使用 SELinux 和 Smack 加強輕量級容器

http://www.bitscn.com/os/linux/200904/158771.html 安全 Linux 容器實現指南 輕量級容器 又稱做 Virtual Private Servers (VPS) 或 Jails,它們是常常用於限制不可信應用程序或用戶的工具。可是最近構造的輕量級容器沒有提供充分的安全保證。使用 SELinux 或 Smack 策略加強這些容器以後,就能夠在 Linux中實現更加安全的容器。本文介紹如何建立受 Linux 安全模塊保護的更加安全的容器。SELinux 和 Smack 策略的開發都在進行當中,而且在各自社區的幫助下不斷獲得改善。 人們聽到容器時的第一反應是 「如何才能建立安全的容器?」。本文經過使用 Linux 安全模塊(Linux Security Modules,LSM)加強容器的安全性來解決這個問題。本文特別演示瞭如何設定安全目標,並經過 Smack 和 SELinux 安全模塊實現目標。 要了解 Linux 容器的背景知識,請閱讀 「linux.chinaitlab.com/administer/777960_2.html" target=_blank>LXC:Linux 容器工具」(developerWorks,2009 年 2 月)。 Linux 容器是根據幾種 Linux 技術構建的概念性工件: 資源名稱空間 容許在容器內部查找進程、文件、SYSV IPC 資源、網絡接口等等。 控制組(Control groups)容許限制放置到容器中的資源。 功能綁定(Capability bounding)設置 限制容器的訪問特權。 必須協調使用這些技術,以實現符合設想的容器。目前已有兩個項目提供這個功能: Libvirt 是可以使用 Xen 管理程序、qemu 模擬器、kvmis 甚至是輕量級容器建立虛擬機的大型項目。 Liblxc 是一個較小的庫和用戶空間命令集合,它們的目的之一是幫助內核開發人員快速輕鬆地測試容器的功能。 由於 「LXC:Linux 容器工具」 是基於 liblxc 編寫的,因此我在這裏繼續使用 liblxc;不過這裏完成的操做也可以使用 libvirt 的容器支持輕鬆完成。 主要元素 1:LSM 在開始以前,若是不太瞭解 LSM,如今能夠快速瀏覽一下。根據 Wikipedia 中的定義:Linux Security Modules (LSM) 是一個容許 Linux 內核支持各類計算機安全模型的框架,同時也避免依賴於特定安全實現。這個框架由 GNU General Public License 條款受權使用,而且是 Linux 2.6 以後的 Linux 內核的標準部分。設計 LSM 的目的是爲成功實現強制訪問控制模塊提供一切必要元素,同時最小化對 Linux 內核的更改。LSM 避免了 Systrace 中的系統調用插入,由於它不支持多處理器內核,而且容易受 TOCTTOU (race) 攻擊。相反,當某個用戶級別的系統將要訪問重要的內部內核對象(好比 inode 和任務控制塊)時,LSM 將在內核中插入 「鉤子(hook)」(向上調用模塊)。這個項目專門用於解決訪問控制問題,以免對主流內核進行大量的複雜修改。該項目並不打算成爲通用的 「鉤子」 或 「向上調用」 機制,也不支持虛擬化。LSM 訪問控制的目標與解決系統審計問題密切相關,但又有所區別。審計要求記錄每次訪問嘗試。LSM 不能解決這個問題,由於這須要大量的鉤子,以檢測內核 「短路」 故障系統在什麼地方發出調用,並在接近重要對象時返回錯誤代碼。 系統安全包括兩個有些衝突的目標。第一個目標是實現完整的細粒度訪問控制。必須對有可能泄露或損壞信息的位置實施控制。過於粗粒度的控制和不進行控制沒有區別。例如,若是必須將全部文件歸爲一種類型,而且有任何一個文件是公開的,則全部文件都是公開的。 另外一方面,配置必須簡單,不然管理員就須要管理不少訪問(可是再次強調,這和不進行控制是同樣的)。例如,若是使程序正常工做須要大量訪問規則,那麼管理員就會爲程序添加許多訪問權限,而不是測試這些訪問規則是否有必要。 Linux 中的兩個基本安全模塊使用不一樣的方法來平衡這個矛盾。 SELinux 首先對全部東西實施控制,同時使用強大的策略語言簡化策略的管理。 Smack 主要提供簡單的訪問控制。 主要元素 2:SELinux 到目前爲止,SELinux 是針對 Linux 的最有名的 MAC 系統(強制訪問控制)。儘管仍然有人反對它,但流行的 Fedora? 發行版從幾年前開始就和 SELinux 一塊兒部署,這是它取得成功的有力證實。 SELinux 使用模塊化策略語言配置,所以用戶能夠輕鬆更新已安裝的策略。這種語言還提供一些接口,容許使用更高級的語句表達一組低級的語句。 在本文中,咱們將使用一個新的接口來定義容器。雖然爲容器添加許多訪問權限使接口自己變得很是大,可是使用接口建立新的容器卻很簡單。這個接口頗有但願成爲核心發佈策略的一部分。 主要元素 3:Smack Smack 是簡化的強制訪問控制內核(Simplified Mandatory Access Control Kernel)的縮寫。它首先使用簡單的文本標籤標記全部進程、文件和網絡流量。使用建立進程的標籤建立最新的文件。一般存在一些帶有明肯定義的訪問規則的默認類型。進程經常能夠對具備同一標籤的對象進行讀寫。繞過 Smack 訪問規則的特權由 POSIX 功能控制,所以帶有 CAP_MAC_OVERRIDE 的任務能夠覆蓋規則;帶有 CAP_MAC_ADMIN 的任務能夠更改規則和標籤。「POSIX file capabilities: Parceling the power of root」(參考資料)演示了這些特權。 受 SELinux 保護的容器 咱們將在容器上使用的 SELinux 策略包含一個 策略模塊;這個模塊已經發布到 refpolicy -- SELinux Reference Policy 開發郵件列表。將這個策略分別下載到 /root/vs 目錄下的 vs.if、vs.fc 和 vs.te 文件中。像下面這樣編譯和安裝新的模塊: cp vm.img selinux.img cp vm.img smack.img 而後使用 lxc-debian 建立 /vs1 and /vs2 容器,而且使用 mkdir /vs1; cd /vs1 lxc-debian create container name: vs1 hostname: vs1 address: 10.0.2.21 gateway: 10.0.2.2 arch: 2 (i386) mkdir /vs2; cd /vs2 lxc-debian create container name: vs2 hostname: vs2 address: 10.0.2.22 gateway: 10.0.2.2 arch: 2 (i386) fixfiles relabel /vs1 fixfiles relabel /vs2 從新標記它們的文件系統。 在啓動容器時(例如經過使用命令 lxc-start -n vs1),極可能會收到一些關於 SELinux 訪問拒絕的審計消息。但不要擔憂 —— 容器將正常啓動,而且會啓用網絡服務並 隔離容器。若是在啓動容器以前使用 mount --bind / /vs1/rootfs.vs1/mnt 幫助容器 vs1 進行假裝,那麼您將會發現,即便是根用戶,也會重用 ls /mnt/root。 爲了瞭解其中的原理,咱們看看 vs.if 接口文件。這個文件定義一個稱爲 container 的接口,它帶有一個參數(即容器將要定義的基本名稱)。vs.te 文件使用容器名 vs1 和 vs2 兩次調用這個函數。在這個接口中,$1 被擴展到這個參數,所以當咱們調用 container(vs1) 時,$1_t 就會變成 vs1_t(從這裏開始,假設咱們定義的是 vs1)。 包含 vs1_exec_t 內容的行是最重要的。這個容器以 vs1_t 類型運行。當 unconfined_t 執行容器的 /sbin/init(類型爲 vs1_exec_t)時,它將進入這種類型。 剩餘的策略主要是授與容器充分的特權,以訪問系統的各個部分:網絡端口、設備和控制檯等。該接口很長,這是由現有 SELinux 引用策略的細粒度特性決定的。正如咱們將要看到的同樣,受 Smack 保護的容器具備更加簡單的策略;可是它在系統服務行爲失誤時提供的靈活保護要少得多。 還有一件事情須要作。須要注意的是,雖然容器不可以重寫它的 $1_exec_t(即 /sbin/init),但它可以執行 mv /sbin /sbin.bak mkdir /sbin touch /sbin/init 生成的 /sbin/init 的類型爲 vs1_file_t。容器管理員爲何須要這樣作呢?由於它會在 unconfined_t 域中啓動容器,包括 ssh daemon,這使他可以得到有特權的 shell,而且可以繞過咱們將要實施的 SELinux 限制。 爲了不這樣作,須要經過定製腳本實際啓動容器,並在啓動容器前將 sbin/init 從新標記爲 vs1_exec_t。事實上,若是容器管理員不介意的話,能夠將一個 init 原始副本複製回容器中並從新標記它。但咱們僅從新標記現有的 init: cat >> /vs1/vs1.sh << EOF #!/bin/sh chcon -t vs1_exec_t /vs1/rootfs.vs1/sbin/init lxc-start -n vs1 EOF chmod u+x /vs1/vs1.sh 如今須要使用 /vs1/vs1.sh 啓動容器,而不是使用 lxc-start 手工啓動。 受 Smack 保護的容器 在啓用 Smack 時從新編譯內核。您必須可以進入 /root/rpmbuild/BUILD/kernel*/linux* 目錄的 make menuconfig,而後轉到 security 菜單禁用 SELinux 並啓用 Smack。接下來僅需重複步驟 make && make modules_install && make install。 此外,也要中止用戶空間對 SELinux 的配置。這能夠在 SELinux 管理 GUI 上實現,或編輯 /etc/selinux/config 並設置 SELINUX=disabled。要在引導時安裝 Smack 策略還須要幾個步驟: mkdir /smack cd /usr/src wget http://schaufler-ca.com/data/080616/smack-util-0.1.tar tar xf smack-util-0.1.tar; cd smack-util-0.1 make && cp smackload /bin 實際的 Smack 策略相似於清單 1: 清單 1. smackaccesses vs1 _ rwa _ vs1 rwa vs2 _ rwa _ vs2 rwa _ host rwax host _ rwax 應該將它複製到一個名爲 /etc/smackaccesses 的文件中。下次運行 /bin/container_setup.sh 時會將這個文件加載到 smackload。 這個策略十分簡單。默認狀況下,任何標籤均可以讀取標記有 _ 的數據。咱們爲容器不能訪問的主機的私有數據定義一個新標籤 host;而且將這個標籤應用到 container_setup.sh 腳本中的 cgroups 文件系統。其餘敏感文件,好比 /etc/shadow,應該使用這個標籤。 咱們對 vs1 和 vs2 進行定義以標記容器。默認狀況下,它們可以訪問本身的數據。咱們添加一個規則使它們能夠寫 _,從而容許發送網絡包。因爲 vs1 不能訪問 vs2 數據(反之亦然),所以容器之間是彼此獨立的。 如前所述,由 CAP_MAC_ADMIN 和 CAP_MAC_OVERRIDE 功能決定定義或繞過 Smack 規則的能力。所以,容器不該該具備這些能力。這能夠經過 helper 程序 dropmacadmin.c 來實現(參見 下載 小節)。必須靜態地編譯它,由於來自主機的容器有不一樣的版本: gcc -o dropmacadmin dropmacadmin.c -static cp dropmacadmin /bin/ 建立一個稱爲 vs1 的新容器: mkdir /vs1; cd /vs1 lxc-debian create container name: vs1 hostname: vs1 address: 10.0.2.21 router: 10.0.2.2 arch: 2 (i386) 使用標籤 vs1 標記 vs1 文件系統中的全部文件: for f in `find /vs1/rootfs.vs1`; do attr -S -s SMACK64 -V vs1 $f done 如今須要建立一個可以安全啓動容器的腳本。這意味着它能將本身的進程標籤設置爲 vs1,並經過 dropmacadmin 打包容器的 /sbin/init。以下所示: cat >> /vs1/vs1.sh << EOF #!/bin/sh cp /bin/dropmacadmin /vs1/rootfs.vs1/bin/ attr -S -s SMACK64 -V vs1 /vs1/rootfs.vs1/bin/dropmacadmin echo vs1 > /proc/self/attr/current lxc-start -n vs1 /bin/dropmacadmin /sbin/init EOF chmod u+x /vs1/vs1.sh 再作一件事情就可讓 vs1 在其即將裝載的 tmpfs 文件系統上執行寫操做: sed -i 's/defaults/defaults,smackfsroot=vs1,smackfsdef=vs1/' \ /vs1/rootfs.vs1/etc/fstab 這致使在 /dev/shm 上裝載 tmpfs 文件系統,以帶上 vs1 標籤,從而讓 vs1 能夠對它執行寫操做。不然,vs1 init 腳本將不能在設置網絡時建立須要使用的 /dev/shm/network 目錄。相似地,若是但願使用基於 ram 的 /tmp,使用相同的選項便可。 如今,咱們再次幫助 vs1 進行假裝。像建立 vs1 那樣建立 vs2,在每一個步驟中將 vs1 替換爲 vs2。而後在 vs1 的 /mnt 下綁定裝載根文件系統: mount --bind /vs1 /vs1 mount --make-runbindable /vs1 mount --rbind / /vs1/rootfs.vs1/mnt 使用 vs1.sh 啓動容器。注意,您還能夠從 kvm 主機看到 vs1 和 vs2 上的 Web 頁面。此外還要注意,vs1 不能經過網絡訪問 vs2。它也不能查看 vs2 的文件: vs1:~# ls /mnt/ (directory listing) vs1:~# ls /mnt/vs2/rootfs.vs2 ls:/mnt/vs2/rootfs.vs2: Permission denied vs1:~# mkdir /cgroup vs1:~# mount -t cgroup cgroup /cgroup vs1:~# ls /cgroup ls:/mnt/vs3: Permission denied vs1:~# mknod /dev/sda1 b 8 1 mknod: `/dev/sda1': Operation not permitted vs1:~# mount /mnt/dev/sda1 /tmp mount: permission denied 它能查看主機文件系統。對於須要保護的任何東西,可使用 host 標籤進行標記。在 cgroup 文件系統上就採用了這種作法,這正是 ls /cgroup 失敗的緣由。 最後,設備白名單 cgroup 防止咱們建立磁盤設備,或在它存在的狀況下裝載它(由於這須要經過 /mnt 來完成)。 固然,咱們的設置方式讓容器管理員能夠刪除 /mnt/dev/sda1,或用其餘方法擾亂主機,所以除了用於演示外,這種綁定裝載是不如人意的! 注意,在 SELinux 系統上,默認(且容易的)路由容許容器經過網絡彼此進行對話,而在 Smack 中則剛好相反。目前,容許容器彼此對話仍是比較困難的。不久之後,將能夠在 IP 地址上設置標籤,而且容許創建策略以實現容器之間的通訊。 在如何創建 Smack 網絡方面還有另外一個問題。命令 kill -9 -1 終止系統上的每一個任務。當這個操做由容器中的任務執行時,它將僅終止同一容器中的任務。這種行爲已經在上游內核中獲得修復,但咱們使用的 Fedora 10 內核還存在該問題。所以,每一個任務都會發出一個 -9 信號。 在受 SELinux 保護的容器中,SELinux 阻止該信號經過容器邊界,所以 kill -9 -1 其實是安全的。但在 Smack 中,默認狀況下任務被標記爲 _(就像網絡同樣),所以因爲咱們容許容器執行 _ 寫操做以寫到網絡中,而且終止任務在 Smack 中被認爲是寫訪問,因此容許容器管理員在整個系統上終止任何任務。 另外一個缺點(SELinux 容器仍然存在該缺點)與 Unix98 僞終端有關。打開兩個圖形化終端。在第一個終端中,啓動 vs1 並查看 /dev/pts。您將看見至少兩個條目(0 和 1),它們分別屬於每一個終端。能夠從 vs1 容器寫入到與另外一個終端對應的條目。 對於 Fedora 內核,有兩個解決方案。可使用設備白名單 cgroup 拒絕容器打開設備。可是這必須在容器每次啓動時手動操做,以容許它訪問本身的終端;或者應用 SELinux 和 Smack 標籤,結果是同樣的。 更新的 2.6.29 內核支持 devpts 名稱空間。容器必須從新裝載 /dev/pts,在這個操做以後,它將不能訪問屬於主機或其餘容器的 devpts 條目。 結束語 本文介紹了構建受 LSM 保護的容器所需的工具,但還有不少工做須要作: 對於 Smack,必須選擇須要標記爲 host 的文件。 對於 SELinux,應該對其進行調優,而後將一個 container 接口放置到上游引用策略。 儘管這些工做正在進行當中,在得到更多關於受 LSM 保護的容器的經驗以前,您不該該徹底信賴這些機制來阻止不可信的根用戶。 儘管目前在建立容器方面尚未最佳實踐,但仍然有一些想法是值得考慮的。首先,記住您正試圖實現兩個有些矛盾的目標:最小化容器(以及主機)之間的複製,同時須要確保安全隔離。實現這些目標的方法之一是,建立一個最小的完整 rootfs,其中不運行任何容器,而且將它的類型標記爲全部容器均可以讀取的類型。而後使用 lxc-sshd 腳本的定製版本建立每一個基於原型的實際容器,覺得容器的大部分文件系統建立只讀裝載,同時爲容器提供一個能夠存儲文件的私有可寫位置(好比 /scratch)。因爲每一個容器都有一個私有的裝載名稱空間,因此它可以綁定裝載任何私有的和/或對於其私有共享目錄可寫的文件或目錄。例如,若是它須要一個私有 /lib,則能夠執行 mount --bind /scratch/rootfs/lib /lib。一樣地,管理員也能確保每一個容器都在啓動時執行 mount --bind /scratch/shadow /etc/shadow。 對於 SELinux 和 Smack,我演示的這個方法的一個明顯缺點就是容器管理員不能在本身的容器的內部利用 LSM 控制信息流。而且爲簡單起見,容器中的全部任務都使用 MAC 策略統一處理。在另外一篇文章中,我將探討如何容許容器管理員指定本身的 LSM 策略,同時又可以約束它們。
相關文章
相關標籤/搜索