Docker安全管理

富士山fy發表於2020-12-03

一、Docker 容器與虛擬機器的區別

1.1 隔離與共享

虛擬機器通過新增 Hypervisor 層,虛擬出網路卡、記憶體、CPU 等虛擬硬體,再在其上建立 虛擬機器,每個虛擬機器都有自己的系統核心。而 Docker 容器則是通過隔離的方式,將檔案系 統、程式、裝置、網路等資源進行隔離,再對許可權、CPU 資源等進行控制,最終讓容器之間互不影響,容器無法影響宿主機。容器與宿主機共享核心、檔案系統、硬體等資源。

1.2 效能與損耗

與虛擬機器相比,容器資源損耗要少。 同樣的宿主機下,能夠建立容器的數量要比虛擬機器多。但是,虛擬機器的安全性要比容器稍好,要從虛擬機器攻破到宿主機或其他虛擬機器,需要 先攻破 Hypervisor 層,這是極其困難的。而 docker 容器與宿主機共享核心、檔案系統等資源,更有可能對其他容器、宿主機產生影響。

二、Docker 存在的安全問題

2.1 Docker 自身漏洞

作為一款應用 Docker 本身實現上會有程式碼缺陷。CVE官方記錄Docker歷史版本共有超過20項漏洞。黑客常用的攻擊手段主要有程式碼執行、許可權提升、 資訊洩露、許可權繞過等。目前 Docker 版本更迭非常快,Docker 使用者最好將 Docker 升級為最新版本。

2.2 Docker 原始碼問題

Docker 提供了 Docker hub,可以讓使用者上傳建立的映象,以便其他使用者下載,快速搭 建環境。但同時也帶來了一些安全問題。例如下面三種方式:
● 黑客上傳惡意映象 如果有黑客在製作的映象中植入木馬、後門等惡意軟體,那麼環境從一開始就已經不安全了,後續更沒有什麼安全可言。

● 映象使用有漏洞的軟體 Docker Hub 上能下載的映象裡面,75%的映象都安裝了有漏洞的軟體。所以下載映象後,需要檢查裡面軟體的版本資訊,對應的版本是否存在漏洞,並及時更新打上補丁。

● 中間人攻擊篡改映象 映象在傳輸過程中可能被篡改,目前新版本的 Docker 已經提供了相應的校驗機制來預 防這個問題。

三、Docker 架構缺陷與安全機制

Docker 本身的架構與機制就可能產生問題,例如這樣一種攻擊場景,黑客已經控制了宿主機上的一些容器,或者獲得了通過在公有云上建立容器的方式,然後對宿主機或其他容器發起攻擊。

3.1 容器之間的區域網攻擊

主機上的容器之間可以構成區域網,因此針對區域網的 ARP 欺騙、嗅探、廣播風暴等攻 擊方式便可以用上。所以,在一個主機上部署多個容器需要合理的配置網路,設定 iptable 規則。

3.2 DDoS 攻擊耗盡資源

Cgroups 安全機制就是要防止此類攻擊的,不要為單一的容器分配過多的資源即可避免此類問題。

3.3 有漏洞的系統呼叫

Docker與虛擬機器的一個重要的區別就是Docker與宿主機共用一個作業系統核心。
一旦宿主核心存在可以越權或者提權漏洞,儘管Docker使用普通使用者執行,在容器被入侵時,攻擊者還可以利用核心漏洞跳到宿主機做更多的事情。

3.4 共享root使用者許可權

如果以 root 使用者許可權執行容器,容器內的 root 使用者也就擁有了宿主機的root許可權。

四、Docker 安全基線標準

從6 個方面總結 Docker 安全基線標準:核心、主機、網路、映象、容器以及其它等等。

4.1 核心級別

(1)及時更新核心。
(2)User NameSpace(容器內的 root 許可權在容器之外處於非高許可權狀態)。
(3)Cgroups(對資源的配額和度量)。
(4)SELiux/AppArmor/GRSEC(控制檔案訪問許可權)。
(5)Capability(許可權劃分)。
(6)Seccomp(限定系統呼叫)。
(7)禁止將容器的名稱空間與宿主機程式名稱空間共享。

4.2 主機級別

(1)為容器建立獨立分割槽。
(2)僅執行必要的服務。
(3)禁止將宿主機上敏感目錄對映到容器。
(4)對 Docker 守護程式、相關檔案和目錄進行審計。
(5)設定適當的預設檔案描述符數。
(檔案描述符:核心(kernel)利用檔案描述符(file descriptor)來訪問檔案。檔案描述符是非負整數。
開啟現存檔案或新建檔案時,核心會返回一個檔案描述符。讀寫檔案也需要使用檔案描述符來指定待讀寫的檔案)
(6)使用者許可權為 root 的 Docker 相關檔案的訪問許可權應該為 644 或者更低許可權。
(7)週期性檢查每個主機的容器清單,並清理不必要的容器。

4.3 網路級別

(1)通過 iptables 設定規則實現禁止或允許容器之間網路流量。
(2)允許 Docker 修改 iptables。
(3)禁止將 Docker 繫結到其他 IP/Port 或者 Unix Socket。
(4)禁止在容器上對映特權埠。
(5)容器上只開放所需要的埠。
(6)禁止在容器上使用主機網路模式。
(7)若宿主機有多個網路卡,將容器進入流量繫結到特定的主機網路卡上。

4.4 映象級別

(1)建立本地映象倉庫伺服器。
(2)映象中軟體都為最新版本。
(3)使用可信映象檔案,並通過安全通道下載。
(4)重新構建映象而非對容器和映象打補丁。
(5)合理管理映象標籤,及時移除不再使用的映象。
(6)使用映象掃描。
(7)使用映象簽名。

4.5 容器級別

(1)容器最小化,作業系統映象最小集。
(2)容器以單一主程式的方式執行。
(3)禁止 privileged 標記使用特權容器。
(4)禁止在容器上執行 ssh 服務。
(5)以只讀的方式掛載容器的根目錄系統。
(6)明確定義屬於容器的資料碟符。
(7)通過設定 on-failure 限制容器嘗試重啟的次數,容器反覆重啟容易丟失資料。
(8)限制在容器中可用的程式樹,以防止 fork bomb。(fork炸彈,迅速增長子程式,耗盡系統程式數量)

4.6 其他設定

(1)定期對宿主機系統及容器進行安全審計。
(2)使用最少資源和最低許可權執行容器。
(3)避免在同一宿主機上部署大量容器,維持在一個能夠管理的數量。
(4)監控 Docker 容器的使用,效能以及其他各項指標。
(5)增加實時威脅檢測和事件響應功能。
(6)使用中心和遠端日誌收集服務

五、容器最小化

如果僅在容器中執行必要的服務,像 SSH 等服務是不能輕易開啟去連線容器的。通常使用以下方式來進入容器。

[root@localhost ~]# docker exec -it d92a84f127ef bash

六、Docker remote api 訪問控制

Docker的遠端呼叫 API 介面存在未授權訪問漏洞,至少應限制外網訪問。建議使用 Socket 方式訪問。
監聽內網 ip,docker daemon 啟動方式如下。

[root@master ~]# vim /usr/lib/systemd/system/docker.service

14 #ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
15 ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://192.168.112.10:2375#開放本地監聽地址和埠
:wq
[root@master ~]# systemctl daemon-reload 
[root@master ~]# systemctl restart docker
[root@master ~]# netstat -anpt | grep docker
tcp        0      0 192.168.112.10:2375     0.0.0.0:*               LISTEN      46579/dockerd       

● 在宿主機的 firewalld 上做 IP 訪問控制即可。

#source address 是客戶端地址
[root@master ~]# firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="192.168.112.20" port protocol="tcp" port="2375" accept"
success
[root@master ~]# firewall-cmd --reload
success
[root@master ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              bc9a0695f571        8 days ago          133MB

● 客戶端操作實現遠端呼叫

[root@client ~]# docker -H tcp://192.168.112.10 images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              bc9a0695f571        8 days ago          133MB

七、限制流量流向

使用防火牆過濾器限制 Docker 容器的源 IP 地址範圍與外界通訊。

firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4" source address="192.168.112.0/24" reject"

大量問題是因為Docker容器埠外放引起的漏洞,除了作業系統賬戶許可權控制上的問題,更在於對Docker Daemon的程式管理上存在隱患。目前常用的Docker版本都支援Docker Daemon管理宿主iptables的,而且一旦啟動程式加上-p host_port:guest_port的埠對映,Docker Daemon會直接增加對應的FORWARD Chain並且-j ACCEPT,而預設的DROP規則是在INPUT鏈做的,對docker沒法限制,這就留下了很嚴重的安全隱患了。因此建議:

  1. 不在有外網ip的機器上使用Docker服務

  2. 使用k8s等docker編排系統管理Docker容器

  3. 宿主上Docker daemon啟動命令加一個–iptables=false,然後把常用iptables寫進檔案裡,再用iptables-restore去刷。

八、映象安全

Docker 映象安全掃描,在映象倉庫客戶端使用證書認證,對下載的映象進行檢查。
通過與 CVE 資料庫同步掃描映象,一旦發現漏洞則通知使用者處理,或者直接阻止映象繼續構建。

如果公司使用的是自己的映象源,可以跳過此步;否則,至少需要驗證 baseimage 的 md5 等特徵值,確認一致後再基於 baseimage 進一步構建。
一般情況下,要確保只從受信任的庫中獲取映象,並且不建議使用–insecure-registry=[] 引數,推薦使用harbor私有倉庫。

九、Docker-TLS加密通訊

9.1 TLS簡介

安全傳輸層協議(TLS)用於在兩個通訊應用程式之間提供保密性和資料完整性。
該協議由兩層組成: TLS 記錄協議(TLS Record)和 TLS 握手協議(TLS Handshake)。

9.2 為什麼要用TLS加密

為了防止鏈路劫持、會話劫持等問題導致 Docker 通訊時被中間人攻擊,c/s 兩端應該通過加密方式通訊。

9.3 TLS安全加密實戰

實驗準備
兩臺裝有Docker的伺服器

伺服器IP地址
master192.168.112.10
client192.168.112.20

● 建立資料夾並修改主機名

[root@localhost ~]# hostnamectl set-hostname master
[root@localhost ~]# su
[root@master ~]# mkdir tls
[root@master ~]# cd tls/
[root@master tls]# ls

● 增加本地對映

[root@master tls]# vim /etc/hosts
127.0.0.1 master

● 建立ca金鑰

[root@master tls]# openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus
...............................++
..................................................................++
e is 65537 (0x10001)
Enter pass phrase for ca-key.pem:#輸入密碼123123
Verifying - Enter pass phrase for ca-key.pem:#輸入密碼123123
[root@master tls]# ls
ca-key.pem

● 建立ca證書

[root@master tls]# openssl req -new -x509 -days 1000 -key ca-key.pem -sha256 -subj "/CN=*" -out ca.pem
Enter pass phrase for ca-key.pem:#輸入密碼123123
[root@master tls]# ls
ca-key.pem  ca.pem

● 建立伺服器私鑰

[root@master tls]# openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
.................................................++
.................................................................++
e is 65537 (0x10001)
[root@master tls]# ls
ca-key.pem  ca.pem  server-key.pem

● 簽名私鑰

[root@master tls]# openssl req -subj "/CN=*" -sha256 -new -key server-key.pem -out server.csr
[root@master tls]# ls
ca-key.pem  ca.pem  server.csr  server-key.pem

● 使用ca證書與私鑰證書籤名,輸入密碼123123

[root@master tls]# openssl x509 -req -days 1000 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem
Signature ok
subject=/CN=*
Getting CA Private Key
Enter pass phrase for ca-key.pem:#輸入密碼123123
[root@master tls]# ls
ca-key.pem  ca.pem  ca.srl  server-cert.pem  server.csr  server-key.pem

● 生成客戶端金鑰

[root@master tls]# openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
...........++
.....++
e is 65537 (0x10001)
[root@master tls]# ls
ca-key.pem  ca.pem  ca.srl  key.pem  server-cert.pem  server.csr  server-key.pem

● 簽名客戶端

[root@master tls]# openssl req -subj "/CN=client" -new -key key.pem -out client.csr
[root@master tls]# ls
ca-key.pem  ca.pem  ca.srl  client.csr  key.pem  server-cert.pem  server.csr  server-key.pem

● 建立配置檔案

[root@master tls]# echo extendedKeyUsage=clientAuth > extfile.cnf
[root@master tls]# ls
ca-key.pem  ca.pem  ca.srl  client.csr  extfile.cnf  key.pem  server-cert.pem  server.csr  server-key.pem

● 簽名證書,輸入密碼123123,需要(簽名客戶端,ca證書,ca金鑰)

[root@master tls]# openssl x509 -req -days 1000 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:
[root@master tls]# ls
ca-key.pem  ca.srl    client.csr   key.pem          server.csr
ca.pem      cert.pem  extfile.cnf  server-cert.pem  server-key.pem

● 刪除多餘檔案

[root@master tls]# rm -rf ca.srl client.csr extfile.cnf server.csr
[root@master tls]# ls
ca-key.pem  ca.pem  cert.pem  key.pem  server-cert.pem  server-key.pem

● 配置docker

[root@master tls]# pwd
/root/tls
[root@master tls]# vim /lib/systemd/system/docker.service
14 #ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
15 ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/root/tls/ca.pem --tlscert=/root/tls/server-cert.pem     --tlskey=/root/tls/server-key.pem -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock

● 重啟程式、重啟docker服務

[root@master tls]# systemctl daemon-reload 
[root@master tls]# systemctl restart docker
[root@master tls]# netstat -anpt | grep docker
tcp6       0      0 :::2376                 :::*                    LISTEN      46590/dockerd       

● 將 /tls/ca.pem /tls/cert.pem /tls/key.pem 三個檔案複製到另一臺主機

[root@master tls]# scp ca.pem root@192.168.112.20:/etc/docker/
root@192.168.112.20's password: #主機的開機密碼
ca.pem                                                                    100% 1765   947.9KB/s   00:00    
[root@master tls]# scp key.pem root@192.168.112.20:/etc/docker/
root@192.168.112.20's password: #主機的開機密碼
key.pem                                                                   100% 3247     1.8MB/s   00:00    
[root@master tls]# scp cert.pem root@192.168.112.20:/etc/docker/
root@192.168.112.20's password: #主機的開機密碼
cert.pem                                                                  100% 1696   893.9KB/s   00:00    

● 本地驗證

[root@master tls]# docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H tcp://master:2376 version
Client: Docker Engine - Community
 Version:           19.03.13
 API version:       1.40
 Go version:        go1.13.15
 Git commit:        4484c46d9d
 Built:             Wed Sep 16 17:03:45 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.13
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       4484c46d9d
  Built:            Wed Sep 16 17:02:21 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.3.7
  GitCommit:        8fba4e9a7d01810a393d5d25a3621dc101981175
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

● 在client上操作

[root@client ~]# vim /etc/hosts

192.168.112.10 master
[root@client ~]# cd /etc/docker/
[root@client docker]# docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H tcp://master:2376 version
Client: Docker Engine - Community
 Version:           19.03.14
 API version:       1.40
 Go version:        go1.13.15
 Git commit:        5eb3275d40
 Built:             Tue Dec  1 19:20:42 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.13
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       4484c46d9d
  Built:            Wed Sep 16 17:02:21 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.3.7
  GitCommit:        8fba4e9a7d01810a393d5d25a3621dc101981175
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

● 在客戶端下載nginx映象,去服務端檢視
在這裡插入圖片描述
在這裡插入圖片描述

相關文章