docker selinux-enabled作用


一.現象

在docker中有一個運行選項是selinux-enabled。這個選項的作用是啥?

簡而言之,它提供了對docker容器中進程的selinux的控制支持。下面舉例說明。

首先按照官方文檔的說明在docker的啟動參數中加入selinux-enabled的支持,當然,前提是你系統的selinux已經打開(https://docs.docker.com/engine/reference/commandline/dockerd/)。

修改完參數,重新部署完畢后,執行以下指令,這條指令啟動一個image為alpine的容器,並且將Host的根目錄掛載到容器的/host目錄。

[wlh@xiaomi ~]$ sudo docker run -ti -v /:/host alpine sh
/ # cd /host/home/
/host/home # ls
ls: can't open '.': Permission denied
/host/home # 

這里可以看到容器沒有權限訪問Host的/home目錄。

然后,作為對比,我們再執行一條指令。

[wlh@xiaomi ~]$ sudo docker run -ti -v /:/host --security-opt label:disable alpine sh
[sudo] password for wlh: 
/ # cd /host/home/
/host/home # ls
lost+found  wlh
/host/home # 

這一次我們可以訪問到宿主機的/home目錄了。

 

二. 推理

那么造成訪問權限不同的原因是什么呢?

這是第一次訪問被拒絕的容器的sh進程的信息(用ps auxZ|grep sh獲取)

system_u:system_r:container_t:s0:c157,c808 root 3289 1.0  0.0 1596  4 pts/2    Ss+  15:02   0:00 sh

這是第二次訪問通過的容器的sh進程的信息(用ps auxZ|grep sh獲取)

system_u:system_r:spc_t:s0      root      2904  0.0  0.0   1596     4 pts/2    Ss+  14:56   0:00 sh

我們可以看到,它們的selinux label是不同的。這些label決定了它們對主機上資源的訪問權限。

第二個容器因為有--security-opt label:disable標簽,所以其進程的selinux label和docker關閉了selinux-enabled時是一樣的。

在如同第一個容器的默認情況下,每次創建一個新的容器,docker會根據/etc/selinux/targeted/contexts/lxc_contexts

文件的內容給容器的進程打上相應的label:

 

process = "system_u:system_r:container_t:s0"
content = "system_u:object_r:virt_var_lib_t:s0"
file = "system_u:object_r:container_file_t:s0"
ro_file="system_u:object_r:container_ro_file_t:s0"
sandbox_kvm_process = "system_u:system_r:svirt_qemu_net_t:s0"
sandbox_kvm_process = "system_u:system_r:svirt_qemu_net_t:s0"
sandbox_lxc_process = "system_u:system_r:container_t:s0"

 

 這里應該是按照sandbox_lxc_process來打Label的。(process和sandbox_lxc_process的內容是一致的,不確定是哪個)

並且隨機生成一個Category,在我們的例子中,Category是c157,c808。因此整個Label為:

system_u:system_r:container_t:s0:c157,c808 

docker每創建一個容器,都會給它分配不同的Category,以此來確保容器之間的訪問隔離。也就是說容器A無法訪問容器B的資源。

 

三.驗證

回到我們前文舉得例子,執行指令驗證容器一的label能否訪問home目錄(sesearch指令可以通過sudo yum install setools-console.x86_64安裝):

宿主機的home目錄的selinux label為:

 

system_u:object_r:home_root_t:s0 home 

 

 下面我們驗證一下selinux的規則對容器訪問的隔離。簡單說明一下這里指令的含義:

-A表示allow,意思是只輸出allow的規則

-s表示的是source,意思是我想要查詢的規則的source是container_t

-t表示的是type,意思是我想要查詢的規則的object是home_root_t。

連在一起的意思是請返回允許domain 為container_t 的進程訪問type為home_root_t的資源的規則。

[root@xiaomi contexts]# sesearch -A -s container_t -t home_root_t |grep read
[root@xiaomi contexts]# sesearch -A -s spc_t -t home_root_t |grep read|grep dir
allow files_unconfined_type file_type:dir { add_name append audit_access create execmod execute getattr ioctl link lock map mounton open quotaon read relabelfrom relabelto remove_name rename reparent rmdir search setattr swapon unlink write };
allow named_filetrans_domain home_root_t:dir { add_name getattr ioctl lock open read remove_name search write };
allow named_filetrans_domain mountpoint:dir { add_name getattr ioctl lock open read remove_name search write };

從中可以看出容器一的lable是不具備讀取宿主機home目錄的權限的,而容器二的label是具備讀取宿主機home目錄的權限的。(關於這里為什么grep出來的結果不包含spc_t/home_root_t這里不展開了,和selinux的attribute機制有關,簡而言之file_type是一個attribute,它是一組type的集合,其中包含了home_root_t)

因此,容器一訪問宿主機的home目錄失敗了,容器二訪問宿主機的home目錄成功了。

 

四.如何確保訪問?

那么問題來了,在一個開啟了selinux的docker服務中,如果我們想要創建一個容器,給它掛載一個宿主機的目錄,我們如何確保容器對該目錄的訪問呢?

做法是在容器啟動時在需要掛載的目錄后面加上z或者Z,例如:

  docker run -v /var/db:/var/db:Z rhel7 /bin/sh

 這里加上大寫Z之后,docker服務會將docker容器一模一樣的selinux lable打到掛載的目錄上

[wlh@xiaomi ~]$ sudo docker run -ti -v /home/wlh:/host alpine sh
[sudo] password for wlh: 
/ # cd /host/
/host # ls
ls: can't open '.': Permission denied
/host # exit
[wlh@xiaomi ~]$ sudo docker run -ti -v /home/wlh:/host:Z alpine sh
/ # cd /host/
/host # ls
file1 file2
/host # exit [wlh@xiaomi ~]$ ls -Z /home/ system_u:object_r:lost_found_t:s0 lost+found system_u:object_r:container_file_t:s0:c498,c862 wlh [wlh@xiaomi ~]$

從中上面的例子可以看出,第一次啟動容器沒有添加大寫Z,一如所料我們沒有權限訪問/home/wlh目錄下的內容。

而加了大寫Z后,我們有權限訪問了,與此同時/home/wlh的selinux label也被改變了。

[root@xiaomi contexts]# sesearch -A -s container_t -t container_file_t |grep read|grep dir
allow container_domain container_file_t:dir { add_name create getattr ioctl link lock map open read relabelfrom relabelto remove_name rename reparent rmdir search setattr unlink write };
allow svirt_sandbox_domain container_file_t:dir { add_name create execmod getattr ioctl link lock map mounton open read relabelfrom relabelto remove_name rename reparent rmdir search setattr unlink write };

訪問selinux policy,確實有規則負責這個事情。

而加上小寫的z的話,情況為:

[wlh@xiaomi ~]$ sudo docker run -ti -v /home/wlh:/host:z alpine sh
[sudo] password for wlh: 
/ # cd /host/
/host # ls
file1 file2/host # exit
[wlh@xiaomi ~]$ ls -Z /home/
    system_u:object_r:lost_found_t:s0 lost+found  system_u:object_r:container_file_t:s0 wlh

注意它和大寫的Z的區別,小寫的z沒有添加category,這應該意味着如果添加的是小寫z,那么該目錄可以被多個容器共享。

而如果添加的是大寫的Z,那么容器掛載的目錄由當前容器獨占的,其他容器無法訪問。(當然,除非其他容器也用大寫Z搶占了該目錄。例如容器A用大寫Z的方式掛載了/home目錄,此時它是可以正常訪問的,接着容器B用大寫Z的方式掛在了/home目錄,那么/home目錄的category會發生變化,由容器A的category轉變為容器B的category,此時容器A失去了/home目錄的訪問權,轉而容器B獲得/home目錄的訪問權)

 

五.總結

根據前文的描述,我想selinux-enabled選項的基本功能已經比較明晰了,它主要是利用selinux機制限制docker容器內的進程訪問宿主機/其它容器的資源。如果這個選項沒有開啟,那么容器自身的隔離機制是用戶安全的唯一屏障,某些惡意程序在某些情況下可能會突破docker容器本身的資源隔離機制(以rootfs為主),訪問到宿主機的資源。開啟了這個選項后,即使惡意程序突破了rootfs的限制進入到宿主機的文件系統中,它所造成的破壞也是有限的,因為selinux機制限制了這個進程對宿主機文件的訪問。

 

ref: https://medium.com/lucjuggery/docker-selinux-30-000-foot-view-30f6ef7f621(這個例子簡單的說明了如何開啟selinux-enabled並且有一個簡單的小例子說明selinux-enabled是干什么的)

  https://prefetch.net/blog/2017/09/30/using-docker-volumes-on-selinux-enabled-servers/(這篇文章主要說明了大寫Z和小寫v的作用)

  http://www.projectatomic.io/blog/2015/06/using-volumes-with-docker-can-cause-problems-with-selinux/(同上)

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM