歡迎光臨
每天分享高質量文章

淺談Docker安全性支援

Docker作為最重視安全的容器技術之一,在很多方面都提供了強安全性的預設配置,其中包括:容器root使用者的Capability能力限制、Seccomp系統呼叫過濾、Apparmor的 MAC 訪問控制、ulimit限制、pid-limits的支援,映象簽名機制等。這篇文章我們就帶大家詳細瞭解一下。
Docker利用Namespace實現了6項隔離,看似完整,實際上依舊沒有完全隔離Linux資源,比如/proc、/sys、/dev/sd*等目錄未完全隔離,SELinux、time、syslog等所有現有Namespace之外的資訊都未隔離。 其實Docker在安全性上也做了很多工作,大致包括下麵幾個方面:
1、Linux核心 Capability 能力限制
Docker支援為容器設定Capabilities,指定開放給容器的許可權。這樣在容器中的root使用者比實際的root少很多許可權。Docker 在0.6版本以後支援將容器開啟超級許可權,使容器具有宿主機的root許可權。
2、映象簽名機制
Docker 1.8版本以後提供了映象簽名機制來驗證映象的來源和完整性,這個功能需要手動開啟,這樣映象製作者可以在push映象前對映象進行簽名,在映象pull的時候,Docker不會pull驗證失敗或者沒有簽名的映象標簽。
3、Apparmor的MAC訪問控制
Apparmor可以將行程的許可權與行程Capabilities能力聯絡在一起,實現對行程的強制性訪問控制(MAC)。在Docker中,我們可以使用Apparmor來限制使用者只能執行某些特定命令、限制容器網路、檔案讀寫許可權等功能。
4、Seccomp系統呼叫過濾
使用Seccomp可以限制行程能夠呼叫的系統呼叫(system call)的範圍,Docker提供的預設Seccomp配置檔案已經禁用了大約44個超過300+的系統呼叫,滿足大多數容器的系統呼叫訴求。
5、User Namespace隔離
Namespace為執行中行程提供了隔離,限制他們對系統資源的訪問,而行程沒有意識到這些限制,為防止容器內的特權升級攻擊的最佳方法是將容器的應用程式配置為作為非特權使用者執行,對於其行程必須作為容器中的root使用者執行的容器,可以將此使用者重新對映到Docker主機上許可權較低的使用者。對映的使用者被分配了一系列UID,這些UID在名稱空間內作為從0到65536的普通UID執行,但在主機上沒有特權。
6、SELinux
SELinux主要提供了強制訪問控制(MAC),即不再是僅依據行程的所有者與檔案資源的rwx許可權來決定有無訪問能力。能在攻擊者實施了容器突破攻擊後增加一層壁壘。Docker提供了對SELinux的支援。
7、pid-limits的支援
在說pid-limits前,需要說一下什麼是fork炸彈(fork bomb),fork炸彈就是以極快的速度建立大量行程,並以此消耗系統分配予行程的可用空間使行程表飽和,從而使系統無法執行新程式。說起行程數限制,大家可能都知道ulimit的nproc這個配置,nproc是存在坑的,與其他ulimit選項不同的是,nproc是一個以使用者為管理單位的設定選項,即他調節的是屬於一個使用者UID的最大行程數之和。這部分內容下一篇會介紹。Docker從1.10以後,支援為容器指定–pids-limit 限制容器內行程數,使用其可以限制容器內行程數。
8、其他核心安全特性工具支援
在容器生態的周圍,還有很多工具可以為容器安全性提供支援,比如可以使用Docker bench audit tool(工具地址:https://github.com/docker/docker-bench-security)檢查你的Docker執行環境,使用Sysdig Falco(工具地址:https://sysdig.com/opensource/falco/)來檢測容器內是否有異常活動,可以使用GRSEC 和 PAX來加固系統核心等等。
Linux核心Capability能力限制

Capabilities簡單來說,就是指開放給行程的許可權,比如允許行程可以訪問網路、讀取檔案等。Docker容器本質上就是一個行程,預設情況下,Docker會刪除必須的Capabilities外的所有Capabilities,可以在Linux手冊頁 中看到完整的可用Capabilities串列。Docker 0.6版本以後支援在啟動引數中增加–privileged選項為容器開啟超級許可權。
Docker支援Capabilities對於容器安全意義重大,因為在容器中我們經常會以root使用者來執行,使用Capability限制後,容器中的root比真正的root使用者許可權少得多。這就意味著,即使入侵者設法在容器內獲取了root許可權,也難以做到嚴重破壞或獲得主機root許可權。
當我們在docker run時指定了–privileded選項,Docker其實會完成兩件事情:
  1. 獲取系統root使用者所有能力賦值給容器;

  2. 掃描宿主機所有裝置檔案掛載到容器內。

下麵我們給大家實際演示一下:
當執行docker run 時未指定–privileded選項:
lynzabo@ubuntu:~$ docker run --rm --name def-cap-con1 -d alpine /bin/sh -c "while true;do echo hello; sleep 1;done"
f216f9261bb9c3c1f226c341788b97c786fa26657e18d7e52bee3c7f2eef755c
lynzabo@ubuntu:~$ docker inspect def-cap-con1 -f '{{.State.Pid}}'
43482
lynzabo@ubuntu:~$ cat /proc/43482/status | grep Cap
CapInh:    00000000a80425fb
CapPrm:    00000000a80425fb
CapEff:    00000000a80425fb
CapBnd:    00000000a80425fb
CapAmb:    0000000000000000
lynzabo@ubuntu:~$
lynzabo@ubuntu:~$ docker exec def-cap-con1 ls /dev
core  fd  full  mqueue  null  ptmx  pts  random  shm  stderr  stdin  stdout  tty  urandom  zero  ...總共15條
lynzabo@ubuntu:~$

如果指定了–privileded選項:
lynzabo@ubuntu:~$ docker run --privileged --rm --name pri-cap-con1 -d alpine /bin/sh -c "while true;do echo hello; sleep 1;done"
ad6bcff477fd455e73b725afe914b82c8aa6040f36326106a9a3539ad0be03d2
lynzabo@ubuntu:~$ docker inspect pri-cap-con1 -f '{{.State.Pid}}'
44312
lynzabo@ubuntu:~$ cat /proc/44312/status | grep Cap
CapInh:    0000003fffffffff
CapPrm:    0000003fffffffff
CapEff:    0000003fffffffff
CapBnd:    0000003fffffffff
CapAmb:    0000000000000000
lynzabo@ubuntu:~$ docker exec pri-cap-con1 ls /dev
agpgart  autofs  bsg  btrfs-control  bus  core  cpu_dma_latency  cuse  dmmidi  dri  ecryptfs
...總共186條
lynzabo@ubuntu:~$

對比/proc/$pid/status ,可以看到兩個容器行程之間能力點陣圖的差別,加上–privileged的能力點陣圖與超級使用者的能力點陣圖一樣。再對比增加–privileged後目錄/dev下檔案變化,可以看到,加了特權後,宿主機所有裝置檔案都掛載在容器內。
我們可以看到,使用–privileged引數授權給容器的許可權太多,所以需要謹慎使用。如果需要掛載某個特定的裝置,可以透過–device方式,只掛載需要使用的裝置到容器中,而不是把宿主機的全部裝置掛載到容器上。例如,為容器內掛載宿主機音效卡:
$ docker run --device=/dev/snd:/dev/snd …

此外,可以透過–add-cap和–drop-cap這兩個引數來對容器的能力進行調整,以最大限度地保證容器使用的安全。
例如,給容器增加一個修改系統時間的命令:
$ docker run --cap-drop ALL --cap-add SYS_TIME ntpd /bin/sh

檢視容器PID,執行getpcaps PID檢視行程的能力,執行結果如下:
[root@VM_0_6_centos ~]# getpcaps 652
Capabilities for `652': = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_time,...
[root@VM_0_6_centos ~]#

可以看到容器中已經增加了sys_time能力,可以修改系統時間了。
Docker映象簽名機制

當我們執行docker pull映象的時候,映象倉庫再驗證完使用者身份後,會先傳回一個manifest.json檔案,其中包含了映象名稱、tag、所有layer層SHA256值,還有映象的簽名資訊,然後docker daemon會並行的下載這些layer層檔案。Docker 1.8以後,提供了一個數字簽名機制——content trust來驗證官方倉庫映象的來源和完整性,簡單來說就是映象製作者製作映象時可以選擇對映象標簽(tag)進行簽名或者不簽名,當pull映象時,就可以透過這個簽名進行校驗,如果一致則認為資料源可靠,並下載映象。
預設情況下,這個content trust是被關閉了的,你需要設定一個環境變數來開啟這個機制,即:
$ export DOCKER_CONTENT_TRUST=11

當content trust機制被開啟後,Docker不會pull驗證失敗或者沒有簽名的映象標簽。當然也可以透過在pull時加上–disable-content-trust來暫時取消這個限制。
Apparmor的MAC訪問控制

AppArmor和SELinux都是Linux安全模組,可以將行程的許可權與行程capabilities能力聯絡在了一起,實現對行程的強制性訪問控制(MAC)。由於SELinux有點複雜,經常都被人直接關閉,而AppArmor就相對要簡單點。Docker官方也推薦這種方式。
Docker自動為容器生成並載入名為docker-default的預設配置檔案。在 Docker 1.13.0和更高版本中,Docker二進位制檔案在tmpfs中生成該配置檔案,然後將其載入到核心中。在早於1.13.0的Docker版本上,此配置檔案將在/etc/apparmor.d/docker中生成。docker-default配置檔案是執行容器的預設配置檔案。它具有適度的保護性,同時提供廣泛的應用相容性。
註意:這個配置檔案用於容器而不是Docker守護行程。執行容器時會使用docker-default策略,除非透過security-opt選項改寫。
下麵我們使用Nginx做演示,提供一個自定義AppArmor配置檔案:
1、建立自定義配置檔案,假設檔案路徑為 /etc/apparmor.d/containers/docker-nginx。
#include 

profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
  #include 
  ...
  deny network raw,
  ...
  deny /bin/** wl,
  deny /root/** wl,
  deny /bin/sh mrwklx,
  deny /bin/dash mrwklx,
  deny /usr/bin/top mrwklx,
  ...
}

2、載入配置檔案:
$ sudo apparmor_parser -r -W /etc/apparmor.d/containers/docker-nginx

3、使用這個配置檔案執行容器:
$ docker run --security-opt "apparmor=docker-nginx" -p 80:80 -d --name apparmor-nginx nginx12

4、進入執行中的容器中,嘗試一些操作來測試配置是否生效:
$ docker container exec -it apparmor-nginx bash1
root@6da5a2a930b9:~# ping 8.8.8.8
ping: Lacking privilege for raw socket.

root@6da5a2a930b9:/# top
bash: /usr/bin/top: Permission denied

root@6da5a2a930b9:~# touch ~/thing
touch: cannot touch 'thing': Permission denied

root@6da5a2a930b9:/# sh
bash: /bin/sh: Permission denied

可以看到,我們透過AppArmor配置檔案可以對容器進行保護。
Seccomp系統呼叫過濾

Seccomp是Linux kernel從2.6.23版本開始所支援的一種安全機制,可用於限制行程能夠呼叫的系統呼叫(system call)的範圍。在Linux系統裡,大量的系統呼叫(systemcall)直接暴露給使用者態程式,但是,並不是所有的系統呼叫都被需要,而且不安全的程式碼濫用系統呼叫會對系統造成安全威脅。透過Seccomp,我們限製程式使用某些系統呼叫,這樣可以減少系統的暴露面,同時使程式進入一種“安全”的狀態。每個行程進行系統呼叫(system call)時,kernel都會檢查對應的白名單以確認該行程是否有許可權使用這個系統呼叫。從Docker 1.10版本開始,Docker安全特性中增加了對Seccomp的支援。
使用Seccomp的前提是Docker構建時已包含了Seccomp,並且核心中的CONFIG_SECCOMP已開啟。可使用如下方法檢查內核是否支援Seccomp:
$ cat /boot/config-`uname -r` | grep CONFIG_SECCOMP=
CONFIG_SECCOMP=y

預設的Seccomp配置檔案為使用Seccomp執行容器提供了一個合理的設定,並禁用了大約44個超過300+的系統呼叫。它具有適度的保護性,同時提供廣泛的應用相容性。預設的Docker配置檔案可以在moby原始碼profiles/seccomp/下找到。
預設seccomp profile片段如下:
{
 "defaultAction""SCMP_ACT_ERRNO",
 "archMap": [
  {
   "architecture""SCMP_ARCH_X86_64",
   "subArchitectures": [
    "SCMP_ARCH_X86",
    "SCMP_ARCH_X32"
   ]
  },=
  ...
 ],
 "syscalls": [
  {
   "names": [
    "reboot"
   ],
   "action""SCMP_ACT_ALLOW",
   "args": [],
   "comment""",
   "includes": {
    "caps": [
     "CAP_SYS_BOOT"
    ]
   },
   "excludes": {}
  },
  ...
 ]
}

seccomp profile包含3個部分:預設操作,系統呼叫所支援的Linux架構和系統呼叫具體規則(syscalls)。對於每個呼叫規則,其中name是系統呼叫的名稱,action是發生系統呼叫時Seccomp的操作,args是系統呼叫的引數限制條件。比如上面的“SCMP_ACT_ALLOW”action代表這個行程這個系統呼叫被允許,這個call,允許行程可以重啟系統。
實際上,該配置檔案是白名單,預設情況下阻止訪問所有的系統呼叫,然後將特定的系統呼叫列入白名單。
Seccomp有助於以最小許可權執行Docker容器。不建議更改預設的Seccomp配置檔案。
執行容器時,如果沒有透過–security-opt選項改寫容器,則會使用預設配置。例如,以下顯式指定了一個策略:
$ docker run --rm \
             -it \
             --security-opt seccomp=/path/to/seccomp/profile.json \
             hello-seccomp

Docker的預設Seccomp配置檔案是一個白名單,它指定了允許的呼叫。Docker檔案列舉了所有不在白名單而被有效阻止的重要(但不是全部)系統呼叫以及每個系統呼叫被阻止的原因。
User Namespace隔離

Linux名稱空間為執行中的行程提供了隔離,限制他們對系統資源的訪問,而行程沒有意識到這些限制。為防止容器內的特權升級攻擊的最佳方法是將容器的應用程式配置為非特權使用者執行,對於其行程必須作為容器中的root使用者執行的容器,可以將此使用者重新對映到Docker主機上許可權較低的使用者。對映的使用者被分配了一系列UID,這些UID在名稱空間內作為從0到65536的普通UID執行,但在主機上沒有特權。
重新對映由兩個檔案處理:/etc/subuid 和 /etc/subgid,其中前者關註使用者ID範圍,後者關註使用者組ID範圍。
例如,如下/etc/subuid中的條目:
testuser:231072:65536

這意味著testuser將從231072開始,在後面的65536個整數中按順序為使用者分配一個ID。例如,名稱空間中的UID 231072對映到容器中的UID 0(root),UID 231073對映為UID 1,依此類推。如果某個行程嘗試提升特權到名稱空間外部,則該行程將作為主機上無特權的高數字UID執行,該UID甚至不對映到真實使用者,這意味著該行程完全沒有主機系統的許可權。
在Docker 1.10以後,可以透過在Dockerd啟動引數中指定userns-remap來啟用這個功能。
下麵我們做一下演示:
1、檢視Docker Daemon是否以root使用者身份執行:
lynzabo@ubuntu:~$ ps -ef | grep dockerd
root 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 dockerd
lynzabo@ubuntu:~$

2、執行容器,指定ID命令:
lynzabo@ubuntu:~$ docker run --rm alpine id
Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
4fe2ade4980c: Pull complete 
Digest: 
sha256:621c2f39f8133acb8e64023a94dbdf0d5ca81896102b9e57c0dc184cadaf5528
Status: Downloaded newer image for alpine:latest
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon)...
lynzabo@ubuntu:~$

上面輸出的最後一行顯示容器以root身份執行:uid = 0(root)和gid = 0(root)。
3、執行docker run指定引數–user ,指定容器以當前使用者身份來執行:
lynzabo@ubuntu:~$ id
uid=1000(lynzabo) gid=1000(lynzabo) 
groups=1000(lynzabo)...
lynzabo@ubuntu:~$ docker run --rm --user 1000:1000 
alpine id
uid=1000 gid=1000
lynzabo@ubuntu:~$

可以看到容器使用的我們設定的使用者和組來執行。
有時候,我們更希望容器裡面是以root使用者來執行,但是並不需要具有宿主機上root許可權,可以使用User Namespace做到這些。使用User Namespace,容器中的root使用者會被重新對映到宿主機上一個非特權使用者,這意味著該行程完全沒有主機系統的許可權。
下麵我們帶大家一起演示一下:
1、停止Docker Daemon:
lynzabo@ubuntu:~$ sudo systemctl stop docker
lynzabo@ubuntu:~$

2、指定在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 dockremap
uid=123(dockremap) gid=132(dockremap) groups=132(dockremap)
lynzabo@ubuntu:~$

驗證條目已經新增到了/etc/subuid和/etc/subgid檔案中。
lynzabo@ubuntu:~$ grep dockremap /etc/subuid
dockremap:165536:65536
lynzabo@ubuntu:~$ grep dockremap /etc/subgid
dockremap:165536:65536
lynzabo@ubuntu:~$

如果這些條目不存在,需要以root使用者身份編輯檔案,並且分配起始的UID和GID(在最高的已經分配的值的基礎上加上偏移,65536)。註意不要使範圍重疊。
3、使用docker info命令驗證Docker是否正確啟用了使用者名稱空間支援:

lynzabo@ubuntu:~$ docker info
...
Docker Root Dir: /home/docker/165536.165536
...
lynzabo@ubuntu:~
lynzabo@ubuntu:~$ ls -ld /home/docker/165536.165536
drwx------ 14 165536 165536 4096 Sep 17 21:44 /home/docker/165536.165536
lynzabo@ubuntu:~$ sudo ls -l /home/docker/165536.165536/
total 48
drwx------ 2 165536 165536 4096 Sep 17 21:44 volumes
drwx--x--x 3 root root 4096 Sep 17 21:44 containerd
drwx------ 2 165536 165536 4096 Sep 17 21:44 containers
drwx------ 3 root root 4096 Sep 17 21:44 image
drwxr-x--- 3 root root 4096 Sep 17 21:44 network
drwx------ 4 165536 165536 4096 Sep 17 21:44 overlay2
...
lynzabo@ubuntu:~$

可以看到Docker工作目錄在原有/var/lib/docker/ 目錄下多了一層以“使用者UID.GID”命名的目錄。檢視該目錄下各個子目錄許可權,有些子目錄仍有root擁有,有些子目錄已經繼承了上級目錄許可權。
4、檢視本地映象:

lynzabo@ubuntu:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
lynzabo@ubuntu:~$

可以看到本地沒有任何映象,很奇怪,我們在上面使用的alpine映象消失了。
5、下麵我們以互動樣式執行一個容器,將宿主機的/bin目錄掛載到容器中:
lynzabo@ubuntu:~$ docker run -it --rm -v /bin:/host/bin busybox /bin/sh
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
8c5a7da1afbc: Pull complete 
Digest: 
sha256:cb63aa0641a885f54de20f61d152187419e8f6b159ed11a251a09d115fdff9bd
Status: Downloaded newer image for busybox:latest
# id
uid=0(root) gid=0(root) groups=10(wheel)
#

上面的輸出顯示容器內部是以root使用者的安全背景關係下執行。
6、下麵我們嘗試執行命令:
# rm /host/bin/sh
rm: can't remove 'sh': Permission denied

操作失敗並顯示許可權被拒絕,這是因為要刪除的檔案存在於Docker宿主機的本地檔案系統中,並且容器在其所在的名稱空間之外沒有root訪問許可權。如果未啟用User Namespace,執行相同的操作,操作將成功。
SELinux支援

我們知道系統的使用者主要分為系統管理員與一般使用者,而這兩種身份能否使用系統檔案資源與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:
1、我們在宿主機上開啟SELinux,嘗試啟動一個Nginx容器並將nginx.conf掛載到容器內。
# 檢視系統Selinux是否開啟,及當前樣式,policy
[lynzabo@localhost ~]$ sestatus 
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: enforcing
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown status: allowed
Max kernel policy version: 28
[lynzabo@localhost ~]$
# Docker開啟Selinux
[root@localhost conf]# ps -ef|grep dockerd
root 4401 1 0 08:15 ? 00:00:00 /usr/bin/dockerd --selinux-enabled
root 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 nginx
bbef34e4caa4e8c3a19f9eae5859691e3504731568e7e585108e26aade95be76
[root@localhost conf]#

使用docker ps檢視容器狀態,容器已經退出,退出日誌為“Permission denied”。

[root@localhost conf]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bbef34e4caa4 nginx "nginx -g 'daemon of…" 15 seconds ago Exited (113 seconds ago test-selinux-nginx
[root@localhost conf]# docker logs -f bbef34e4caa4
2018/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 nginx
db49bbe352ff1ab800274a17fd18f9c7d86c281e60ac3ffa36ba14e12949285d
[root@localhost conf]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
db49bbe352ff nginx "nginx -g 'daemon of…" 5 seconds ago Up 2 seconds 80/tcp test-selinux-z-nginx
[root@localhost conf]#

這個時候看到Nginx正常啟動了,說明SELinux審核透過了。
pid-limits的支援

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 top
5693c8c31284b0f3cb4eb10d4f67e13ad98d1972a27dab094f0ad96154a5ce6a
[lynzabo@VM_0_6_centos ~]$ docker exec -ti test-pids-limit sh                             
# ps -ef
PID 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。
其他核心安全特性工具支援

在容器生態的周圍,還有很多工具可以為容器安全性提供支援。
  1. 可以使用docker-bench-security檢查你的Docker執行環境,如Docker daemon配置,宿主機配置

  2. 使用Sysdig Falco(地址:https://sysdig.com/opensource/falco/)可以監視容器的行為,檢測容器中是否有異常活動。

  3. 使用GRSEC 和 PAX來加固系統核心,還可以使用GRSecurity為系統提供更豐富的安全限制。等等。

本文轉載自公眾號:小米生態雲,點選檢視原文

Kubernetes應用實戰培訓

Kubernetes應用實戰培訓將於2018年10月19日在上海開課,3天時間帶你係統學習Kubernetes本次培訓包括:容器特性、映象、網路;Docker特性、架構、元件、概念、Runtime;Docker安全;Docker實踐;Kubernetes架構、核心元件、基本功能;Kubernetes設計理念、架構設計、基本功能、常用物件、設計原則;Kubernetes的實踐、執行時、網路、外掛已經落地經驗;微服務架構、DevOps等,點選下方圖片檢視詳情。

贊(0)

分享創造快樂