天天看點

淺析Docker運作安全

一、docker run 文法

文法:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]           

複制

二、 Docker 運作安全相關參數

2.1 啟用 AppArmor

AppArmor 主要的作用是設定某個可執行程式的通路控制權限,可以限制程式 讀/寫某個目錄/檔案,打開/讀/寫網絡端口等等。

Apparmor 的配置檔案儲存在/etc/apparmor.d/containers/目錄下

配置檔案使用官方文檔下的 Nginx 配置執行個體,見https://docs.docker.com/engine/security/apparmor/

加載一個新的配置檔案

$ sudo apparmor_parser -r -W /etc/apparmor.d/containers/docker-nginx           

複制

上傳新的配置檔案

$ apparmor_parser -R /path/to/profile           

複制

在 Docker 裡使用 AppArmor

$ docker run --security-opt "apparmor=docker-nginx" -p 80:80 -d --name apparmor-nginx nginx           

複制

2.2 啟用 SELinux

配置步驟

在主控端上啟用 SELinux,Docker 守護程序啟用 SELinux,預設啟動容器就開啟了 SELinux

[root@localhost selinux]# 
sestatusSELinux 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: 
31[root@localhost selinux]# docker info...init version: fec3683Security Options:seccompWARNING: 
You're not using the default seccomp profileProfile: 
/etc/docker/seccomp/default-no-chmod.jsonselinuxusernsKernel Version: 
3.10.0-1062.12.1.el7.x86_64...           

複制

測試,挂載主控端

/

目錄到容器的

/hacking

目錄

[root@localhost selinux]# docker run -it --rm -v 
/:/hacking centos:latest /bin/shsha256:fe8d824220415eed5477b63addf40fb06c3b049404242b31982106ac204f6700Status: 
Downloaded newer image for centos:latestsh-4.4# cd /sh-4.4# lsbin dev etc hacking home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr varsh-4.4# cd hacking/sh-4.4# lsbin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr varsh-4.4# cd var/logsh-4.4# lsls: cannot open directory '.': Permission denied           

複制

運作參數可以選擇 SELinux 的級别,标簽包含 4 個部分

User:Role:Type:level

,可以設定 SELinux 不同的标簽設定運作級别。

docker run --security-opt label=type:spc_t replicated
docker run --interactive --tty --security-opt label=level:TopSecret centos /bin/bas
docker run -it --security-opt label:disable alpine sh           

複制

标簽

[root@localhost ~]# ls /etc/selinux/targeted/contexts/files/file_contexts file_contexts.homedirs file_contexts.subs mediafile_contexts.bin file_contexts.homedirs.bin file_contexts.subs_dist[root@localhost ~]# cat /etc/selinux/targeted/contexts/files/file_contexts           

複制

2.3 限制運作容器的核心功能

Linux核心能夠将root使用者的特權分解為稱為功能的不同單元。例如,CAP_CHOWN功能允許root使用者對檔案UID和GID進行任意更改。CAP_DAC_OVERRIDE功能允許root使用者繞過檔案讀取,寫入和執行操作的核心權限檢查。與Linux root使用者相關的幾乎所有特殊功能都分解為單獨的功能。

更細粒度的功能限制可以:

從 root 使用者帳戶中删除單個功能,使其功能/危險性降低。

以非常精細的級别向非root使用者添加特權。

功能适用于檔案和線程。檔案功能允許使用者以更高的特權執行程式。這類似于setuid位的工作方式。線程功能跟蹤正在運作的程式中功能的目前狀态。

預設情況下,Docker使用白名單方法删除除所需功能之外的所有功能。

啟動指令

docker run --rm -it --cap-drop $CAP alpine sh
docker run --rm -it --cap-add $CAP alpine sh
docker run --rm -it --cap-drop ALL --cap-add $CAP alpine sh           

複制

$CAP 包含http://man7.org/linux/man-pages/man7/capabilities.7.html,在 Linux 接近40項的 Capabilities 中,Docker為了確定容器的安全,僅僅支援了其中的14項基本的 Capabilities:CAP_CHOWN、CAP_DAC_OVERRIDE、CAP_FSETID、CAP_MKNOD、FOWNER、NET_RAW、SETGID、SETUID、SETFCAP、SETPCAP、NET_BIND_SERVICE、SYS_CHROOT、KILL和AUDIT_WRITE。

測試指令

$ docker container run --rm -it alpine chown nobody /
$ docker container run --rm -it --cap-drop ALL --cap-add CHOWN alpine chown nobody /
$ docker container run --rm -it --cap-drop CHOWN alpine chown nobody /
$ docker container run --rm -it --cap-add chown -u nobody alpine chown nobody /           

複制

2.4 不使用特權模式運作容器

特權模式參數—privileged,運作特權容器時允許容器内使用者直接通路主控端的資源,是以通過濫用特權容器,攻擊者可以擷取主控端資源的通路權限。特權容器産生後,由于增強權限的許多,攻擊者可能會以root權限運作代碼。這表明攻擊者可以以root權限運作主機,包括CAP_SYS_ADMIN。

攻擊者在擷取了暴露的特權容器通路權限後,就可以進一步發起很多攻擊活動。攻擊者可以識别出主機上運作的軟體,并找出和利用相關漏洞。還可以利用容器軟體漏洞或錯誤配置,比如使用弱憑證或沒有認證的容器。由于攻擊者有root通路權限,是以惡意代碼或挖礦機都可以執行并有效地隐藏。

測試

建立容器:

docker run -d -name centos7 --privileged=true centos:7 /usr/sbin/init           

複制

進入容器:

docker exec -it centos7 /bin/bash           

複制

2.5 限制容器擷取新的特權,使用—security-opt=no-new-privileges

含義如下:

程序可以在核心中設定no_new_privs位,該位在fork,clone和exec之間持續存在。

no_new_privs位可確定該程序或其子程序不會獲得任何其他特權。

設定no_new_privs位後,該程序将無法取消設定。

即使程序使用設定了檔案功能位的setuid二進制檔案或可執行檔案執行,也不允許帶有no_new_privs的程序更改uid / gid或獲得任何其他功能。

no_new_privs還可以防止SELinux之類的Linux安全子產品(LSM)過渡到不允許通路目前程序的程序标簽。這意味着SELinux程序僅允許轉換為具有較少特權的程序類型。

testnnp.c,列印 uid 資訊

#include <stdio.h>#include <unistd.h>#include <sys/types.h>
int main(int argc, char *argv[]){        printf("Effective uid: %d\n", geteuid());        return 0;}           

複制

編譯

[root@localhost ~]# make testnnpcc testnnp.c -o testnnp           

複制

制作鏡像,

dockerfile

FROM fedora:latestADD testnnp /root/testnnpRUN chmod +s /root/testnnpENTRYPOINT /root/testnn           

複制

制作測試鏡像

[root@localhost ~]# docker build -t testnnp .           

複制

測試

# docker run -it --rm --user=1000  testnnp           

複制

使用

no-new-privileges

# docker run -it --rm --user=1000 --security-opt=no-new-privileges testnnp           

複制

2.6 使用 cgroup 確定容器在定義的 cgroup 中運作

不使用—cgroup-parent選項 ,控制組 CGroups 是 Linux 核心的另一個重要特性,主要用來實作對資源的限制和審計等.

淺析Docker運作安全

控制組(cgroup)是Linux核心的一項功能,可讓您限制通路程序和容器對系統資源(如CPU,RAM,IOPS和網絡)的通路權限。

$ docker run -d –name='low_prio' –cpuset-cpus=0 –cpu-shares=20 busybox md5sum /dev/urandom           

複制

2.7 啟用 seccomp

前面講 docker 守護程序安全時,說過 seccomp 是組核心安全政策,不同的政策有不同的名稱,可以在 docker 運作時指定使用的安全政策,而不是使用 docker 守護程序設定的預設政策。seccomp=unconfined表示不啟用 seccomp,下面是不建議的啟動指令。

$ docker container run --rm -it --security-opt seccomp=unconfined debian:jessie sh           

複制

指定 sccomp

$ docker run --rm -it --security-opt seccomp=default-no-chmod.json alpine sh           

複制

2.8 運作容器不挂載主機的系統分區

包括:

/

/boot

/dev

/etc

/lib

/proc

/sys

/usr

2.9 容器根目錄以隻讀方式挂載—read-only

使用—read-only會限制運作容器的跟目錄為隻讀

[root@localhost ~]# docker run -it --read-only 72300a873c2c /bin/bashroot@f077b480dbe5:/# ls bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var root@f077b480dbe5:/# touch 111touch: cannot touch '111': Read-only file system           

複制

如果需要挂載的目錄有讀寫權限可以使用更細的權限控制,如挂載特定目錄有讀寫權限。

docker run --interactive --tty --read-only -v /opt/app/data:/run/app/data:rw centos /bin/bash           

複制

2.10 不挂載主控端的docker.sock到任何容器

淺析Docker運作安全

安裝Docker之後,Docker守護程序會監聽Unix域套接字:/var/run/docker.sock。

下面的指令用于運作容器,并采用互動模式(interactive mode,該模式下會直接進入容器内),同時綁定docker.sock。

# docker run -v /var/run/docker.sock:/var/run/docker.sock -ti alpine sh           

複制

綁定Docker套接字之後,容器的權限會很高,可以控制Docker守護程序。

2.11 不使用共享的挂載傳播模式

不使用—volume=/hostPath:/containerPath:shared的配置

# docker run <Run arguments> --volume=/hostPath:/containerPath:shared <Container Image Name or ID> <Command>           

複制

2.12 不要直接挂載主機裝置,如果需要,設定成隻讀

避免容器内使用者直接修改裝置資訊。

docker run --interactive --tty --device=/dev/tty0:/dev/tty0:rw --device=/dev/temp_sda:/dev/temp_sda:r centos bash           

複制

2.13 on-failure容器重新開機政策設定為 5

通過在docker run指令中使用—restart标志,您可以指定重新開機政策,以指定容器在啟動失敗時應如何重新開機。您應該選擇onfailure重新啟動政策,并将重新啟動嘗試限制為5次。

如果無限期地嘗試啟動容器,則可能導緻主控端上的拒絕服務,尤其是在同一主機上有多個容器的情況下。此外,忽略容器的退出狀态并始終嘗試重新啟動容器,會導緻無法調查導緻容器終止的根本原因。如果某個容器被終止,則應調查其背後的原因,而不僅僅是嘗試無限期地重新啟動它。應該使用失敗時重新啟動政策将容器重新啟動的次數限制為最多5次嘗試。

docker run --detach --restart=on-failure:5 nginx           

複制

2.14 不使用網絡空間共享

—net=hostHost模式并沒有為容器建立一個隔離的網絡環境,該模式下的 Docker 容器會和 host 主控端共享同一個網絡 namespace,是以容器可以和主控端一樣,使用主控端的eth0,實作和外界的通信,特點:

這種模式下的容器沒有隔離的network namespace

容器的 IP 位址同 Docker主機的 IP 位址

要注意容器中服務的端口号不能與Docker主機上已經使用的端口号相沖突

host模式能夠和其它模式共存

2.15 主機程序命名空間不共享,禁用—pid=host

預設下,所有的容器都啟用了PID命名空間。PID命名空間提供了程序的分離。PID命名空間删除系統程序視圖,允許程序ID可重用,包括pid 1。

在一些情況下需要容器共享主機程序命名空間,基本上允許容器内的程序可以檢視主機的所有程序。例如,建構了一個帶調試工具容器,想在容器使用這些工具來調試主機的程序。

如果使用—pid=host參數,容器可以直接操作主控端上的資料。如果 dockerd 守護程序設定了使用者命名空間映射,運作容器時使用該參數會導緻啟動失敗。

docker run --interactive --tty --pid=host centos /bin/bash           

複制

2.16 主機 IPC 命名空間不共享,禁用

--ipc=host

程序與單個”管理程”程序共享記憶體,以便交換資料( 通過使用共享緩沖區) 。這個解決方案是為了性能需求實作的。

--ipc=MODE

:設定容器的IPC模式,

shareable

自己的私有IPC名稱空間,可以與其他容器共享。

host

:使用主機系統的IPC名稱空間。

禁止使用

host

模式,下面是錯誤的示例:

docker run --interactive --tty --ipc=host centos /bin/bash           

複制

可以與其他容器使用共享 IPC

docker run --ipc=container:<id> <image>docker run -d --ipc=shareable data-serverdocker run -d --ipc=container:data-server data-client           

複制

2.17 不共享主機 UTS 命名空間,禁用—uts=host

UTS命名空間用于設定主機名和對該命名空間中正在運作的程序可見的域。預設下,所有的容器,包括那麼以—network=host運作的容器,有它們自己的UTS命名空間。設定UTS為host将使容器使用與主機相同的UTS命名空間。注意—hostname在host UTS模式是無效的。

當你想在主機更改hostname之後,同時也更改同樣的hostname到容器。

2.18 不共享主機使用者命名空間,禁用—users=host

預設情況下,Docker守護程式以root身份運作。這使守護程式可以建立并使用啟動容器所需的核心結構。但是,它也存在潛在的安全風險。

預設的容器以 root 賬号運作

# docker run --rm alpine id
# docker run --rm --user 1000:1000 alpine id
# docker run --rm --privileged --userns=host alpine id           

複制

2.19 限制容器運作時占用記憶體和 CPU

常用于限制 CPU 和記憶體的參數

-c —cpu-shares參數隻能限制容器使用 CPU 的比例

—cpus後面跟着一個浮點數,代表容器最多使用的核數,可以精确到小數點二位,也就是說容器最小可以使用 0.01 核 CPU

-m —memory:容器能使用的最大記憶體大小,最小值為 4m

—memory-swap:容器能夠使用的 swap 大小

2.20 必要時 Docker 運作覆寫預設的ulimit

docker 守護程序可以配置預設的限制,必要時可以使用 docker run 指令覆寫的預設

# docker run --ulimit nofile=1024:1024 --interactive --tty centos /bin/bash           

複制

2.21 使用—pids-limit限制指定時間内生成的程序數

使用PID cgroup參數—pids-limit可以通過限制在指定時間範圍内容器内部可能建立的程序數量來防止邏輯**類的攻擊。

# docker run -it --pids-limit 100 <Image_ID>           

複制

2.22 運作時檢查容器運作狀态,使用—health-cmd參數

用于檢查容器的運作狀态

# docker run -d --name db --health-cmd "curl --fail http://localhost:8091/pools || exit 1" --health-interval=5s --timeout=3s arungupta/couchbase           

複制

2.23 傳入容器的流量綁定特定的主控端端口

指定映射到主控端上特定網絡端口:

docker run --detach --publish 10.2.3.4:49153:80 nginx           

複制

2.24 不使用 docker 預設的橋接網絡 docker0

Docker将以橋接模式建立的虛拟接口連接配接到名為docker0的通用橋接。此預設網絡模型容易受到ARP欺騙和MAC泛洪攻擊,因為沒有對其應用過濾。

實際網絡通常以編排系統的網絡進行配置。

2.25 使用大于 1024 的端口,容器隻映射必須使用的端口

低于 1024 的端口通常用于系統服務,使用低于 1024 的端口可能與主控端服務産生沖突,80 和 443 除外,容器服務對外映射端口應該隻映射必須開放的端口。

2.26 確定Docker指令始終使用其映像的最新版本

使用最新版本的鏡像避免引入漏洞。

2.27 使用最小化的容器,確定不包含多餘的元件或服務

如 SSH、Telnet 或者其他不需要的元件或服務。

2.28 docker exec 指令不使用—privileged選項

在docker exec指令中使用—privileged選項可為指令提供擴充的Linux功能。

2.29 docker exec 指令不使用—user=root選項

在docker exec指令中使用—user=root選項,會以root使用者身份在容器内執行指令。例如,如果容器以tomcat使用者(或任何其他非root使用者)身份運作,則可以使用—user=root選項通過docker exec以root身份運作指令。

三、其他參數說明

[OPTIONS] 參數說明:
—add-host list 添加自定義主機到ip映射(書寫格式為:主機:ip)
-a, —attach list 附加到STDIN、STDOUT或STDERR上
—blkio-weight uint16 Block IO (相對權重),取值10到1000之間,0為禁用(預設0)
—blkio-weight-device list Block IO weight (相對于裝置的權重) (預設為數組的形式)
—cap-add list 添加Linux功能
—cap-drop list 删除Linux功能
—cgroup-parent string 容器的可選父級對照組項
—cidfile string 将容器ID寫入檔案
—cpu-period int 限制CPU CFS(完全公平排程程式)周期
—cpu-quota int 限制CPU CFS(完全公平的排程程式)上限
—cpu-rt-period int 限制CPU運作時周期(以微秒為機關)
—cpu-rt-runtime int 限制CPU實時運作時間(以微秒為機關)
-c, —cpu-shares int CPU 共享 (相對權重的設定)
—cpus decimal 設定cpu的數量
—cpuset-cpus string 允許執行的cpu (0-3,0,1)
—cpuset-mems string 允許執行的MEMs (0-3,0,1)
-d, —detach 在背景運作容器并列印容器ID
—detach-keys string 覆寫分離容器的鍵序列
—device list 向容器添加主機裝置
—device-cgroup-rule list 向 cgroup 允許的裝置清單中添加一個或多個規則
—device-read-bps list 限定裝置的讀取速率(機關: byte/s)(預設為 [])
—device-read-iops list 限定裝置的讀取速率(機關:IO/s)(預設為 [])
—device-write-bps list 限定裝置的寫入速率(機關: byte/s)(預設為 [])
—device-write-iops list 限定裝置的寫入速率(機關:IO/s)(預設為 [])
—disable-content-trust 跳過鏡像驗證(預設為 true)
—dns list 設定自定義DNS伺服器
—dns-option list 設定DNS選項
—dns-search list 設定自定義的DNS搜尋域
—entrypoint string 覆寫鏡像的預設入口點
-e, —env list 設定環境變量
—env-file list 讀取環境變量内容
—expose list 公開一個端口或多個端口
—group-add list 添加其他要加入的組
—health-cmd string 指令運作以檢查健康
—health-interval duration 運作檢查之間的時間(ms
—health-retries int 連續的失敗需要報告不健康
—health-start-period duration 啟動健康重試倒計時前容器初始化的啟動周期(ms
—health-timeout duration 健康檢查運作情況的最大時間值 格式為:(ms
—help 列印出使用情況
-h, —hostname string 定義容器主機名
—init 在容器中運作初始化,以轉發信号并擷取程序
-i, —interactive 即使沒有連接配接,也保持STDIN開放
—ip string 設定容器的 IPv4 位址 (例如,192.168.155.139)
—ip6 string 設定IPv6位址(例如,2001:db8::33)
—ipc string 使用IPC模式
—isolation string 容器隔離技術
—kernel-memory bytes 核心記憶體限制
-l, —label list 在容器上設定中繼資料
—label-file list 在以行分隔的标簽檔案中讀取
—link list 向另一個容器添加連結
—link-local-ip list 容器 IPv4/IPv6 連結本地位址
—log-driver string 設定容器的日志驅動
—log-opt list 設定日志驅動器選項
—mac-address string 配置容器MAC位址(例如,92:d0:c6:0a:29:33)
-m, —memory bytes 設定記憶體限額
—memory-reservation bytes 記憶體軟限制
—memory-swap bytes 交換限制等于記憶體加上交換:’-1’,以啟用無限交換
—memory-swappiness int 優化容器記憶體交換 (0 到 100) (預設為 -1)
—mount mount 将檔案系統挂載附加到容器
—name string 為容器指定一個名稱
—network string 将容器連接配接到網絡
—network-alias list 為容器連接配接的網絡添加别名
—no-healthcheck 禁止任何容器指定 HEALTHCHECK
—oom-kill-disable 禁止OOM事件被殺死
—oom-score-adj int 優化主機的OOM事件 ,參數範圍 (-1000 到 1000)
—pid string 設定PID命名
—pids-limit int 優化容器pid限制(如果設定-1則為無限制)
—privileged 賦予容器擴充的權限
-p, —publish list 将容器的端口釋出到主機
-P, —publish-all 将所有公開的端口釋出到随機端口
—read-only 将容器的根檔案系統挂載為隻讀(後面會詳細講到)
—restart string 配置容器的重新開機政策,當容器退出時重新啟動(預設為“no”)
—rm 當容器退出時自動移除這個容器
—runtime string 使用容器的運作時
—security-opt list 指定docker啟動的安全項
—shm-size bytes /dev/shm 的大小(這個可以使其容量進行動态的擴充)
—sig-proxy 設定代理接收京城信号 (預設為 true)
—stop-signal string 停止容器的信号 (預設為 “SIGTERM”)
—stop-timeout int 設定逾時停止容器(以秒為機關)
—storage-opt list 設定容器的存儲驅動程式選項
—sysctl map 指定系統控制項 (預設為 map[] 的格式)
—tmpfs list 挂載tmpfs目錄
-t, —tty 為目前容器配置設定一個用戶端
—ulimit ulimit 啟動需要限制的項(預設為數組的形式)
-u, —user string 使用者名或UID(格式為: <name
—userns string 使用使用者名稱空間
—uts string 使用UTS名稱空間
-v, —volume list 綁定安裝卷(關于容器卷,在Docker容器資料卷中會具體的講解)
—volume-driver string 容器的可選卷驅動程式
—volumes-from list 指定容器裝載卷
-w, —workdir string 容器内的工作目錄

參考

https://www.mf8.biz/ubuntu-apparmor-openresty/

https://docs.docker.com/engine/security/apparmor/

https://www.4hou.com/posts/4YP2

https://www.freebuf.com/articles/system/201793.html