前面咱們爲你們介紹了Docker支持容器root用戶的 Capability 能力限制、鏡像簽名、Apparmor的MAC訪問控制、使用Seccomp限制系統調用等安全性支持,這篇文章咱們會爲你們介紹Docker其餘安全性特性支持。
linux
上篇文章回顧:淺談Docker的安全性支持(上篇)nginx
Linux 命名空間爲運行中的進程提供了隔離,限制他們對系統資源的訪問,而進程沒有意識到這些限制。爲防止容器內的特權升級攻擊的最佳方法是將容器的應用程序配置爲非特權用戶運行,對於其進程必須做爲容器中的 root 用戶運行的容器,能夠將此用戶從新映射到 Docker 主機上權限較低的用戶。映射的用戶被分配了一系列 UID,這些 UID 在命名空間內做爲從 0 到 65536 的普通 UID 運行,但在主機上沒有特權。
docker
從新映射由兩個文件處理:/etc/subuid 和 /etc/subgid,其中前者關注用戶 ID 範圍,後者關注用戶組 ID 範圍。json
例如,以下 /etc/subuid 中的條目:ubuntu
testuser:231072:65536複製代碼
這意味着 testuser 將從 231072 開始,在後面的 65536 個整數中按順序爲用戶分配一個 ID。例如,命名空間中的 UID 231072 映射到容器中的 UID 0(root),UID 231073 映射爲 UID 1,依此類推。若是某個進程嘗試提高特權到命名空間外部,則該進程將做爲主機上無特權的高數字 UID 運行,該 UID 甚至不映射到真實用戶,這意味着該進程徹底沒有主機系統的權限。centos
在Docker1.10之後,能夠經過在Dockerd啓動參數中指定userns-remap 來啓用這個功能。安全
下面咱們作一下演示:bash
一、查看Docker Daemon是否以root用戶身份運行app
lynzabo@ubuntu:~$ ps -ef | grep dockerdroot 1557 1 0 12:54 ? 00:05:08 /usr/bin/dockerd -H fd://lynzabo 36398 23696 0 21:41 pts/1 00:00:00 grep --color=auto dockerdlynzabo@ubuntu:~$複製代碼
二、運行容器,指定 id 命令
tcp
lynzabo@ubuntu:~$ docker run --rm alpine idUnable to find image 'alpine:latest' locallylatest: Pulling from library/alpine4fe2ade4980c: Pull complete Digest: sha256:621c2f39f8133acb8e64023a94dbdf0d5ca81896102b9e57c0dc184cadaf5528Status: Downloaded newer image for alpine:latestuid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon)...lynzabo@ubuntu:~$複製代碼
上面輸出的最後一行顯示容器以root身份運行:uid = 0(root)和gid = 0(root)。
三、執行docker run 指定參數--user ,指定容器以當前用戶身份來運行
lynzabo@ubuntu:~$ iduid=1000(lynzabo) gid=1000(lynzabo) groups=1000(lynzabo)...lynzabo@ubuntu:~$ docker run --rm --user 1000:1000 alpine iduid=1000 gid=1000lynzabo@ubuntu:~$複製代碼
能夠看到容器使用的咱們設置的用戶和組來運行。
有時候,咱們更但願容器裏面是以root用戶來運行,可是並不須要具備宿主機上root權限,可使用User Namespace作到這些。使用User Namespace,容器中的root用戶會被從新映射到宿主機上一個非特權用戶,這意味着該進程徹底沒有主機系統的權限。
下面咱們帶你們一塊兒演示一下:
一、中止Docker Daemon
lynzabo@ubuntu:~$ sudo systemctl stop dockerlynzabo@ubuntu:~$複製代碼
二、指定在User Namespace模式下運行Docker Daemon
lynzabo@ubuntu:~$ sudo dockerd --userns-remap=default &lynzabo@ubuntu:~$複製代碼
當你將 Docker 配置爲使用 userns-remap 功能時,能夠指定爲現有用戶或組,也能夠指定爲 default。若是指定爲default,則會爲此建立並使用用戶和組 dockremap 。也能夠在 daemon.json 配置文件中指定。
經過 id 命令驗證 Docker 已經建立了這個用戶。
lynzabo@ubuntu:~$ id dockremapuid=123(dockremap) gid=132(dockremap) groups=132(dockremap)lynzabo@ubuntu:~$複製代碼
驗證條目已經添加到了 /etc/subuid 和 /etc/subgid 文件中。
lynzabo@ubuntu:~$ grep dockremap /etc/subuiddockremap:165536:65536lynzabo@ubuntu:~$ grep dockremap /etc/subgiddockremap:165536:65536lynzabo@ubuntu:~$複製代碼
若是這些條目不存在,須要以 root 用戶身份編輯文件,而且分配起始的 UID 和 GID(在最高的已經分配的值的基礎上加上偏移,65536)。注意不要使範圍重疊。
三、使用 docker info 命令驗證Docker是否正確啓用了用戶命名空間支持
lynzabo@ubuntu:~$ docker info...Docker Root Dir: /home/docker/165536.165536...lynzabo@ubuntu:~$ lynzabo@ubuntu:~$ ls -ld /home/docker/165536.165536drwx------ 14 165536 165536 4096 Sep 17 21:44 /home/docker/165536.165536lynzabo@ubuntu:~$ sudo ls -l /home/docker/165536.165536/total 48drwx------ 2 165536 165536 4096 Sep 17 21:44 volumesdrwx--x--x 3 root root 4096 Sep 17 21:44 containerddrwx------ 2 165536 165536 4096 Sep 17 21:44 containersdrwx------ 3 root root 4096 Sep 17 21:44 imagedrwxr-x--- 3 root root 4096 Sep 17 21:44 networkdrwx------ 4 165536 165536 4096 Sep 17 21:44 overlay2...lynzabo@ubuntu:~$複製代碼
能夠看到Docker 工做目錄在原有/var/lib/docker/ 目錄下多了一層以「用戶UID.GID」命名的目錄。查看該目錄下各個子目錄權限,有些子目錄仍有 root 擁有,有些子目錄已經繼承了上級目錄權限。
四、查看本地鏡像
lynzabo@ubuntu:~$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZElynzabo@ubuntu:~$複製代碼
能夠看到本地沒有任何鏡像,很奇怪,咱們在上面使用的alpine鏡像消失了。
五、下面咱們以交互模式運行一個容器,將宿主機的/bin目錄掛載到容器中
lynzabo@ubuntu:~$ docker run -it --rm -v /bin:/host/bin busybox /bin/shUnable to find image 'busybox:latest' locallylatest: Pulling from library/busybox8c5a7da1afbc: Pull complete Digest: sha256:cb63aa0641a885f54de20f61d152187419e8f6b159ed11a251a09d115fdff9bdStatus: Downloaded newer image for busybox:latest/ # iduid=0(root) gid=0(root) groups=10(wheel)/ #複製代碼
上面的輸出顯示容器內部是以root用戶的安全上下文下運行。
六、下面咱們嘗試執行命令
/ # rm /host/bin/shrm: can't remove 'sh': Permission denied複製代碼
操做失敗並顯示權限被拒絕,這是由於要刪除的文件存在於Docker宿主機的本地文件系統中,而且容器在其所在的命名空間以外沒有root訪問權限。若是未啓用User Namespace,執行相同的操做,操做將成功。
咱們知道系統的用戶主要分爲系統管理員與通常用戶,而這兩種身份可否使用系統文件資源與 rwx 的權限設置有關,這種存取文件系統的方式被稱爲「自主式存取控制(DAC)」。不過你要注意的是,各類權限設置對 root 是無效的,這個時候就可使用委任式存取控制(MAC)了,使用MAC能夠針對特定的程序與特定的文件資源來進行權限的控管,也就是說,即便是root用戶,那麼在使用不一樣的程序時,你所能取得的權限並不必定是root,而要根據當時程序的設置而定。
SELinux 就是經過 MAC 的方式來控管程序,他控制的主體是程序, 而目標則是該程序可否讀寫的「文件資源」。下面是使用SeLinux基本流程:
由上圖咱們能夠發現:
(1) 主體程序必需要經過 SELinux 政策內的規則放行後,就能夠與目標資源進行安全性本文的比對。
(2) 比對安全性本文,比對成功就能夠訪問目標,比對失敗,記錄拒絕信息。
SELinux的工做模式一共有三種 Enforcing、Permissive和Disabled :
Enforcing 模式:將受限主體進入規則比對、安全本文比對,若是失敗,抵擋主體程序的讀寫行爲,而且記錄這一行爲。 若是成功,這才進入到 rwx 權限的判斷。
Permissive模式:不會抵擋主體程序讀寫行爲,只是將該動做記錄下來。
Disabled 的模式:禁用SELinux,直接去判斷 rwx。
Docker守護進程的SELinux功能默認是禁用的,須要使用--selinux-enabled來啓用,容器的標籤限制可以使用-security-opt加載SELinux或者AppArmor的策略進行配置。
下面演示使用SELinux:
一、咱們在宿主機上開啓 SELinux,嘗試啓動一個 Nginx 容器並將 nginx.conf 掛載到容器內。
# 查看系統Selinux是否開啓,及當前模式,policy[lynzabo@localhost ~]$ sestatus SELinux status: enabledSELinuxfs mount: /sys/fs/selinuxSELinux root directory: /etc/selinuxLoaded policy name: targetedCurrent mode: enforcingMode from config file: enforcingPolicy MLS status: enabledPolicy deny_unknown status: allowedMax kernel policy version: 28[lynzabo@localhost ~]$# Docker開啓Selinux[root@localhost conf]# ps -ef|grep dockerdroot 4401 1 0 08:15 ? 00:00:00 /usr/bin/dockerd --selinux-enabledroot 4549 3117 0 08:15 pts/0 00:00:00 grep --color=auto dockerd[root@localhost conf]# # 運行一個容器,將本地nginx.conf文件掛載到容器中[root@localhost conf]# docker run --name test-selinux-nginx -v /root/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -d nginxbbef34e4caa4e8c3a19f9eae5859691e3504731568e7e585108e26aade95be76[root@localhost conf]#複製代碼
使用 docker ps 查看容器狀態,容器已經退出,退出日誌爲「Permission denied」
[root@localhost conf]# docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESbbef34e4caa4 nginx "nginx -g 'daemon of…" 15 seconds ago Exited (1) 13 seconds ago test-selinux-nginx[root@localhost conf]# docker logs -f bbef34e4caa42018/09/17 15:16:02 [emerg] 1#1: open() "/etc/nginx/nginx.conf" failed (13: Permission denied)nginx: [emerg] open() "/etc/nginx/nginx.conf" failed (13: Permission denied)[root@localhost conf]#複製代碼
能夠看到錯誤信息好像是權限被拒絕,那麼咱們檢查一下nginx.conf 的權限是否符合咱們的要求。
使用 ls -Z 查看 nginx.conf的 DAC 與 MAC 權限信息。
[root@localhost conf]# ls -Z-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 nginx.conf[root@localhost conf]#複製代碼
文件的權限爲 644。咱們在上面查看 Docker 進程,Docker 進程的權限爲 root,對於644的權限文件是可讀可寫的。 看來,問題應該是出在 MAC 權限上。
分析 ls -Z 的結果,nginx.conf 對應的安全性文本的類型爲 admin_home_t:s0,在啓用SELinux後,咱們的主體是沒法操做這種類型的object的,因此不管 Docker 容器的權限是不是 root,Docker 容器進程都沒有權限讀取宿主上的 nginx.conf。
Docker 官方提供了一種解決方案專門用來解決與 SELinux 相關的權限問題,在將 SELinux 上的文件掛載到容器中時,在掛載的路徑最後加上:z。如:
docker run -v /var/db:/var/db:z rhel7 /bin/sh複製代碼
Docker 會自動將被掛載的宿主目錄的安全性文本配置爲目標可讀。
[root@localhost conf]# docker run --name test-selinux-z-nginx -v /root/nginx/conf/nginx.conf:/etc/nginx/nginx.conf:z -d nginxdb49bbe352ff1ab800274a17fd18f9c7d86c281e60ac3ffa36ba14e12949285d[root@localhost conf]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESdb49bbe352ff nginx "nginx -g 'daemon of…" 5 seconds ago Up 2 seconds 80/tcp test-selinux-z-nginx[root@localhost conf]#複製代碼
這個時候看到Nginx正常啓動了,說明SELinux審覈經過了。
Linux內核會限制全部進程能夠打開的文件總數,同時爲了防止某個進程消耗過多文件資源,也會對單個進程設置限制,這個時候ulimit就派上了用場,使用ulimit命令能夠限制進程最多打開文件句柄數、最多打開進程數、線程棧大小等等。Docker對ulimit也提供了支持,Docker 1.6以前,Docker容器的ulimit設置,繼承自Docker daemon,Docker 1.6以後,既能夠設置全局默認的ulimit,也能夠對單個容器指定ulimit。
以下,指定容器最多可打開文件句柄數爲2048,最多打開100個進程。
[lynzabo@VM_0_6_centos ~]$ docker run -it --ulimit nofile=2048 --ulimit nproc=100 busybox sh / # ulimit -a-f: file size (blocks) unlimited-t: cpu time (seconds) unlimited-d: data seg size (kb) unlimited-s: stack size (kb) 8192-c: core file size (blocks) unlimited-m: resident set size (kb) unlimited-l: locked memory (kb) 64-p: processes 100-n: file descriptors 2048-v: address space (kb) unlimited-w: locks unlimited-e: scheduling priority 0-r: real-time priority 0/ #複製代碼
容器進程數限制坑介紹
提及進程數限制,你們可能都知道ulimit的nproc這個配置,nproc是存在坑的,與其餘ulimit選項不一樣的是,nproc是一個以用戶爲管理單位的設置選項,即他調節的是屬於一個用戶UID的最大進程數之和。以下面輸出:
# 咱們使用daemon用戶啓動4個容器,並設置容許的最大進程數爲3$ docker run -d -u daemon --ulimit nproc=3 busybox top$ docker run -d -u daemon --ulimit nproc=3 busybox top$ docker run -d -u daemon --ulimit nproc=3 busybox top# 這個容器會失敗並報錯,資源不足$ docker run -d -u daemon --ulimit nproc=3 busybox top複製代碼
咱們指定使用daemon用戶來在容器中啓動top進程,結果啓動到第4個容器的時候就報錯了。而實際上,咱們原本是想限制每一個容器裏用戶最多隻能建立3個進程。另外,默認狀況下,Docker在容器中啓動進程是以root用戶身份啓動的,而ulimit的nproc參數是沒法對root用戶進行限制。
Docker從1.10之後,支持爲容器指定--pids-limit 限制容器內進程數,和容器裏用戶無關。以下面例子:
[lynzabo@VM_0_6_centos ~]$ docker run -d --name test-pids-limit --pids-limit=5 busybox top5693c8c31284b0f3cb4eb10d4f67e13ad98d1972a27dab094f0ad96154a5ce6a[lynzabo@VM_0_6_centos ~]$ docker exec -ti test-pids-limit sh / # ps -efPID USER TIME COMMAND 1 root 0:00 top 5 root 0:00 sh 9 root 0:00 ps -ef/ # nohup top &/ # nohup: appending output to nohup.out/ # nohup top &/ # nohup: appending output to nohup.out/ # nohup top &/ # nohup: appending output to nohup.out/ # nohup top &sh: can't fork: Resource temporarily unavailable/ #複製代碼
容器啓動參數中,咱們經過--pids-limit設置容器裏最多隻能運行5個進程,能夠看到,當進程數達到5個後,在啓動進程時就提示can't fork: Resource temporarily unavailable。
在容器生態的周圍,還有不少工具能夠爲容器安全性提供支持。
一、可使用 docker-bench-security檢查你的Docker運行環境,如Docker daemon配置,宿主機配置
二、使用Sysdig Falco(地址:https://sysdig.com/opensource/falco/)能夠監視容器的行爲,檢測容器中是否有異常活動。
三、使用GRSEC 和 PAX來加固系統內核,還可使用GRSecurity爲系統提供更豐富的安全限制。等等。
這篇文章的分享就到這裏,但願本次分享對你們有所幫助,歡迎留言與咱們交流。