淺談Docker的安全性支持(上篇)

Docker做爲最重視安全的容器技術之一,在不少方面都提供了強安全性的默認配置,其中包括:容器root用戶的 Capability 能力限制、Seccomp系統調用過濾、Apparmor的 MAC 訪問控制、ulimit限制、pid-limits的支持,鏡像簽名機制等。這篇文章咱們就帶你們詳細瞭解一下。
nginx

上篇文章回顧:從HBase offheap到Netty的內存管理git


寫在前面

Docker利用Namespace實現了6項隔離,看似完整,實際上依舊沒有徹底隔離Linux資源,好比/proc 、/sys 、/dev/sd*等目錄未徹底隔離,SELinux、time、syslog等全部現有Namespace以外的信息都未隔離。 其實Docker在安全性上也作了不少工做,大體包括下面幾個方面:
github

一、Linux內核 Capability 能力限制docker

Docker支持爲容器設置Capabilities,指定開放給容器的權限。這樣在容器中的root用戶比實際的root少不少權限。Docker 在0.6版本之後支持將容器開啓超級權限,使容器具備宿主機的root權限。json

二、鏡像簽名機制ubuntu

Docker 1.8版本之後提供了鏡像簽名機制來驗證鏡像的來源和完整性,這個功能須要手動開啓,這樣鏡像製做者能夠在push鏡像前對鏡像進行簽名,在鏡像pull的時候,Docker不會pull驗證失敗或者沒有簽名的鏡像標籤。centos

三、Apparmor的MAC訪問控制安全

Apparmor能夠將進程的權限與進程capabilities能力聯繫在一塊兒,實現對進程的強制性訪問控制(MAC)。在Docker中,咱們可使用Apparmor來限制用戶只能執行某些特定命令、限制容器網絡、文件讀寫權限等功能。bash

四、Seccomp系統調用過濾網絡

使用Seccomp能夠限制進程可以調用的系統調用(system call)的範圍,Docker提供的默認 Seccomp 配置文件已經禁用了大約 44 個超過 300+ 的系統調用,知足大多數容器的系統調用訴求。

五、User Namespace隔離

Namespace爲運行中進程提供了隔離,限制他們對系統資源的訪問,而進程沒有意識到這些限制,爲防止容器內的特權升級攻擊的最佳方法是將容器的應用程序配置爲做爲非特權用戶運行,對於其進程必須做爲容器中的 root 用戶運行的容器,能夠將此用戶從新映射到 Docker 主機上權限較低的用戶。映射的用戶被分配了一系列 UID,這些 UID 在命名空間內做爲從 0 到 65536 的普通 UID 運行,但在主機上沒有特權。

六、SELinux

SELinux主要提供了強制訪問控制(MAC),即再也不是僅依據進程的全部者與文件資源的rwx權限來決定有無訪問能力。能在攻擊者實施了容器突破攻擊後增長一層壁壘。Docker提供了對SELinux的支持。

七、pid-limits的支持

在說pid-limits前,須要說一下什麼是fork炸彈(fork bomb),fork炸彈就是以極快的速度建立大量進程,並以此消耗系統分配予進程的可用空間使進程表飽和,從而使系統沒法運行新程序。提及進程數限制,你們可能都知道ulimit的nproc這個配置,nproc是存在坑的,與其餘ulimit選項不一樣的是,nproc是一個以用戶爲管理單位的設置選項,即他調節的是屬於一個用戶UID的最大進程數之和。這部份內容下一篇會介紹。Docker從1.10之後,支持爲容器指定--pids-limit 限制容器內進程數,使用其能夠限制容器內進程數。

八、其餘內核安全特性工具支持

在容器生態的周圍,還有不少工具能夠爲容器安全性提供支持,好比可使用 Docker bench audit tool(工具地址:https://github.com/docker/docker-bench-security)檢查你的Docker運行環境,使用Sysdig Falco(工具地址:https://sysdig.com/opensource/falco/)來檢測容器內是否有異常活動,可使用GRSEC 和 PAX來加固系統內核等等。

此次分享咱們帶你們瞭解一下Docker對前四項是如何安全支持的,下一篇文章會帶你們瞭解剩餘內容。

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其實會完成兩件事情:

  • 獲取系統root用戶全部能力賦值給容器;

  • 掃描宿主機全部設備文件掛載到容器內。

下面咱們給你們實際演示一下:

當執行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"f216f9261bb9c3c1f226c341788b97c786fa26657e18d7e52bee3c7f2eef755clynzabo@ubuntu:~$ docker inspect def-cap-con1 -f '{{.State.Pid}}'43482lynzabo@ubuntu:~$ cat /proc/43482/status | grep CapCapInh:    00000000a80425fbCapPrm:    00000000a80425fbCapEff:    00000000a80425fbCapBnd:    00000000a80425fbCapAmb:    0000000000000000lynzabo@ubuntu:~$lynzabo@ubuntu:~$ docker exec def-cap-con1 ls /devcore  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"ad6bcff477fd455e73b725afe914b82c8aa6040f36326106a9a3539ad0be03d2lynzabo@ubuntu:~$ docker inspect pri-cap-con1 -f '{{.State.Pid}}'44312lynzabo@ubuntu:~$ cat /proc/44312/status | grep CapCapInh:    0000003fffffffffCapPrm:    0000003fffffffffCapEff:    0000003fffffffffCapBnd:    0000003fffffffffCapAmb:    0000000000000000lynzabo@ubuntu:~$ docker exec pri-cap-con1 ls /devagpgart  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 652Capabilities 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 配置文件:

一、建立自定義配置文件,假設文件路徑爲 /etc/apparmor.d/containers/docker-nginx 。

#include <tunables/global>profile docker-nginx flags=(attach_disconnected,mediate_deleted) { #include <abstractions/base> ... deny network raw, ... deny /bin/** wl, deny /root/** wl, deny /bin/sh mrwklx, deny /bin/dash mrwklx, deny /usr/bin/top mrwklx, ...}複製代碼

二、加載配置文件

$ sudo apparmor_parser -r -W /etc/apparmor.d/containers/docker-nginx複製代碼

三、使用這個配置文件運行容器

$ docker run --security-opt "apparmor=docker-nginx" -p 80:80 -d --name apparmor-nginx nginx12複製代碼

四、進入運行中的容器中,嘗試一些操做來測試配置是否生效:

$ docker container exec -it apparmor-nginx bash1root@6da5a2a930b9:~# ping 8.8.8.8ping: Lacking privilege for raw socket.root@6da5a2a930b9:/# topbash: /usr/bin/top: Permission deniedroot@6da5a2a930b9:~# touch ~/thingtouch: cannot touch 'thing': Permission deniedroot@6da5a2a930b9:/# shbash: /bin/sh: Permission denied複製代碼

能夠看到,咱們經過 apparmor 配置文件能夠對容器進行保護。

Seccomp系統調用過濾

Seccomp是Linux kernel 從2.6.23版本開始所支持的一種安全機制,可用於限制進程可以調用的系統調用(system call)的範圍。在Linux系統裏,大量的系統調用(systemcall)直接暴露給用戶態程序,可是,並非全部的系統調用都被須要,並且不安全的代碼濫用系統調用會對系統形成安全威脅。經過Seccomp,咱們限制程序使用某些系統調用,這樣能夠減小系統的暴露面,同時使程序進入一種「安全」的狀態。每一個進程進行系統調用(system call)時,kernel 都會檢查對應的白名單以確認該進程是否有權限使用這個系統調用。從Docker1.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文檔列舉了全部不在白名單而被有效阻止的重要(但不是所有)系統調用以及每一個系統調用被阻止的緣由,詳細內容能夠點擊「Docker

相關文章
相關標籤/搜索