1. 程式人生 > >Docker的安全問題以及一些預防方案

Docker的安全問題以及一些預防方案

這篇文章算是《Using Docker:Chapter13. Security and Limiting Containers》的讀書筆記。

Docker中可能會出現的安全問題

核心漏洞(Kernel exploits)

 容器是基於核心的虛擬化,主機(host)和主機上的所有容器共享一套核心。如果某個容器的操作造成了核心崩潰,那麼反過來整臺機器上的容器都會受到影響。

拒絕服務攻擊(Denial-of-service attacks)

 所有的容器都共享了核心資源,如果一個容器獨佔了某一個資源(記憶體、CPU、各種ID),可能會造成其他容器因為資源匱乏無法工作(形成DoS攻擊)。

容器突破(Container breakouts)

 Linux的namespace機制是容器的核心之一,它允許容器內部擁有一個PID=1的程序而在容器外部這個程序號又是不一樣的(比如1234)。現在問題在於如果一個PID=1的程序突破了namespace的限制,那麼他將會在主機上獲得root許可權。

有毒映象(Poisoned images)

 主要是考慮到映象本身的安全性,沒太多好說的。

金鑰獲取(Compromising secrets)

 容器中的應用可能會獲取一些容器外部的服務,這些服務之間可能會有金鑰(secret key)等,如果因為金鑰儲存不當,那麼這些服務對於攻擊者來說就是可獲取的了,這就會造成很多隱患。特別的,這樣的問題如果出現在微服務架構中就特別嚴重。

安全方案

主機級別的隔離

 主機級別的隔離(Segregate Containers by Host)即將不同使用者的容器放在不同的機器上、將那些存放了敏感資料的容器和普通的容器隔離開來、將哪些直接暴露給終端使用者的容器(web容器)隔離開來。
 主機級別的隔離的好處是可以防止容器突破攻擊、DoS攻擊,但是這樣的隔離會付出成本甚至效能的代價的。

關注映象的安全

 通常,我們獲取映象的方式是從網路中(Docker hub)中pull,就像從網路上下載軟體時下載方會提供一個SHA一樣,我們也可以通過一個數字簽名來檢視下載的映象是否安全。
 從1.8開始,Docker提供了一個數字簽名機制——content trust來確保映象來源的真實可靠。簡單來說就是映象製作者製作映象時可以選擇對映象標籤(tag)進行簽名或者不簽名,當我pull映象時,就可以通過這個簽名進行校驗,如果一致則認為資料來源可靠,並下載映象。
 預設情況下,這個content trust是被關閉了的,你需要設定一個環境變數來開啟這個機制,即:

export DOCKER_CONTENT_TRUST=1
  
  • 1

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

關於docker run的一些細節

 下面介紹一些在寫Dockerfile時的一些細節或者在啟動docker時的一些引數,這些細節或者引數可能用來提高安全性。

在容器中設定一個普通使用者(User)

容器中的應用最好不要以root使用者身份執行,這是為了防止容器突破攻擊。在實踐中,建議在做Dockerfile的時候,要建立一個普通使用者,然後切換到這個使用者上來,比如:

RUN groupadd -r user_grp && useradd -r -g user_grp user
USER user
  
  • 1
  • 2

 或者在最後的入口點(entrypoint)中切換到普通使用者上去,比如

#!/bin/bash
set -e
if [ "$1" = 'redis-server' ]; then
chown -R redis .
exec gosu redis "[email protected]"
fi
exec "[email protected]"
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

 這裡的gosu命令是docker建議在入口點用來代替sudo使用的命令。
 最後,如果非要在容器中使用root使用者,建議使用SELinux來對容器進行約束。

限制容器的網路

 這個比較好理解,第一,容器應向外暴露儘可能少的埠,此外,對於容器之間的通訊,最好是需要通訊的容器才是連通的。
 對於第二點需要稍微解釋一下,一般情況下,即使是容器埠關閉,容器之間還是可以相互通訊的。為了避免這種情況,需要在docker服務(Docker daemon)啟動時指定一個--icc = false標誌位,這個標誌位會關閉容器之間的這種通訊。

移除SUID和SGID的二進位制位

 SUID和SGID就是對於一些指令碼檔案,在執行(指令碼)的時候擁有這個擁有者的許可權,為了防止許可權提升攻擊(privilege escalation attack),我們需要儘可能的移除SUID和SGID的標誌位(許可權標誌是按位來表示的)。
 一般情況下,我們更可能在Dockerfile中使用SUID和SGID的功能(為了在執行指令碼檔案時獲取特root許可權等..),所以建議就是最好在Dockerfile結束前去除這些標誌位(當然要在前面說的切換使用者之前)。一個例子如下:

FROM debian:wheezy
RUN find / -perm +6000 -type f -exec chmod a-s {} \; || true
  
  • 1
  • 2

限制記憶體

 這個自不用說,就是防止一個容器耗盡記憶體資源(DoS的一種吧?),一般使用-m以及--memory-swap標誌位實現。

注意,--memory-swap是等於記憶體加上交換記憶體的總容量!然後如果只是-m,那麼預設的--memory-swap就等於2倍-m,同樣的,如果-m--memory-swap的值設為一樣,就默認了沒有交換記憶體的空間,而且總的容量僅僅是--memory-swap的而不是二者之和。

限制CPU

 同樣是防止CPU資源被耗盡(DoS),相應的啟動引數是

  • -c 預設值是1024,也就是一個權值,當-c為1024時,這個容器使用的cpu數量是(cpu總數/容器數量),以此類推,當-c為2045時,這個值是2*(cpu總數/容器數量)通過以上設定,只會在 CPU 密集(繁忙)型執行程序時體現出來。當一個 container 空閒時,其它容器都是可以佔用 CPU 的。
  • –cpu-period + –cpu-quota 不同於上面的相對分配CPU,這兩個引數合起來使用可以實現對cpu的絕對分配,具體可以參考一篇部落格

限制重啟

 容器如果不停地重啟,也會消耗很多資源,造成DoS攻擊,這個主要是在啟動命令列時使用--restart=on-failure:10這樣的引數,當然這裡的10可以改成其他任意數字。

限制檔案系統

 這個主要是防止在容器內的隨意寫(指令碼)造成的攻擊。即在啟動時戴上--read-only引數。

限制能力(Capabilities)

 capabilities簡單來說,就是開放給程序的許可權(access),比如ping的時候就使用了socket。docker容器本質上就是一個程序,它預設有一些capabilities,例如:CHOWN, DAC_OVERRIDE, FSETID, FOWNER, MKNOD, NET_RAW, SETGID, SETUID, SETFCAP, SETPCAP,
NET_BIND_SERVICE, SYS_CHROOT, KILL, and AUDIT_WRITE(具體的可以 man capabilities)。一般情況下,可能需要根據業務增加capabilities(--cap-add),當然為了安全,可能需要先關掉所有的capabilities(--cap-drop all)再增加一些特定的capabilities。

使用資源限制

 基於Linux核心本身對程式的資源限制ulimit命令(別忘了docker容器本身是個程序),可以使用--ulimit標誌來限制一些資源。主要的限制有cpu、nofile(最大檔案描述符)、nproc(最大程序數),注意資源限制分為硬限制和軟限制,硬限制資源一旦設定不能增加、軟限制設定後可以增加,但是 不能超過硬限制數,格式為:

軟限制[:硬限制]
如:docker run --ulimit cpu=12:14 amouat/stress stress --cpu 1
,在使用了cpu12秒後殺死容器
  
  • 1
  • 2
  • 3

SELinux

SELinux主要提供了強制訪問控制(MAC),即不再是僅依據程序的所有者與檔案資源的rwx許可權來決定有無訪問能力。能在攻擊者實施了容器突破攻擊後增加一層壁壘。建議在主機上開啟SELinux。注意SELinux在紅帽的Linux上是預設開啟的,但是Ubuntu預設沒有安裝SElinux。

AppArmor

 SELinux算是蠻複雜的,經常都被人直接關掉。而AppArmor就相對要簡單點,它將程序的許可權與程序capabilities所聯絡在一起。docker官方也推薦這種方式

總結

 考慮docker的安全性主要還是從以下幾個方面出發

  • docker本身的機制,即基於namespace進行隔離、cgroup進行資源限制、capabilities進行許可權限制
  • 對於docker守護程序本身的攻擊上
  • 在進行檔案配置(做Dockerfile或者啟動容器)時的疏漏
  • Linux核心本身的安全特性

 整體來說,如果你將容器內的應用以非root身份執行,Docker 預設配置下是挺安全的。上面提到的也是在極端情況下的一些考慮,具體的,應該和現實的應用場景相結合。

轉載連結:https://blog.csdn.net/Ruidu_Doer/article/details/53401523

這篇文章算是《Using Docker:Chapter13. Security and Limiting Containers》的讀書筆記。