Rootless 容器技術的興起,讓開發者得以在非 root 許可權下執行容器,有效提升了系統安全性。Docker 的 Rootless 模式需要額外組態,而 Podman 則原生支援 Rootless 模式,簡化了使用流程。Docker Rootless 模式下,網路設定需特別注意名稱空間的差異,而 Podman 則透過 slirp4netns 提供使用者空間網路功能。組態 Podman 時,/etc/subuid 和 /etc/subgid 檔案的設定至關重要,確保容器的 UID 和 GID 對映正確。此外,Falco 等安全工具可進一步強化容器執行時的安全性,監控並防範異常行為。
Rootless 容器執行環境的安全強化
隨著容器技術的發展,安全性和隔離性成為了開發者和維運團隊關注的焦點。Docker 自 v19.03 版本起引入了一個實驗性質的功能,稱為 Rootless Mode,讓 Docker Engine 無需超級使用者(Superuser)許可權即可啟動容器。本文將探討 Rootless Mode 的原理、安裝步驟及其限制,並進一步比較另一個流行的容器執行環境 Podman。
Docker Rootless Mode 的原理與優勢
Docker Rootless Mode 是根據既有的 User Namespaces 功能進行擴充套件。User Namespaces 允許容器內的使用者 ID(UID)和群組 ID(GID)與主機上的 UID/GID 進行對映,但實際上容器內的使用者並未擁有主機上的許可權,從而提升了安全性。Rootless Mode 進一步簡化了這一流程,讓使用者可以在沒有超級使用者許可權的情況下執行 Docker 容器。
啟用 Rootless Mode 的前置條件
在啟用 Rootless Mode 之前,需要確保系統滿足特定的組態要求:
安裝 uidmap 套件:在 Debian 系列的系統上,需要安裝
uidmap套件來支援使用者名稱空間的對映。$ apt install uidmap檢查 subuid 和 subgid 組態:確保非特權使用者擁有足夠的 UID 和 GID 範圍可供對映。通常需要在
/etc/subuid和/etc/subgid中組態。$ cat /etc/subuid chris:100000:65536 $ cat /etc/subgid chris:100000:65536
Rootless Mode 的限制
儘管 Rootless Mode 提供了更高的安全性和便利性,但仍存在一些功能限制,如下表所示:
| 受限功能 | 描述/替代方案 |
|---|---|
| 控制群組(cgroups) | 無法使用 cgroups 來限制容器的 CPU、I/O 和 RAM 使用量。 |
| AppArmor | 在使用 AppArmor 的系統上(如 Ubuntu),無法套用強制存取控制。 |
| 檢查點(Checkpoint) | 無法使用容器的快照功能。 |
| Overlay v1 | 不支援原始的 Overlay 儲存驅動程式,建議使用 Overlay2。 |
| 特權埠 | 無法直接繫結到小於 1024 的特權埠,需使用 setcap 命令進行特殊組態。 |
| Ping 命令 | 在某些 Linux 發行版上,可能需要調整 net.ipv4.ping_group_range 設定才能使用 ping 命令。 |
| 網路設定 | 需要使用 nsenter 命令進入正確的名稱空間才能檢視容器的 IP 位址。 |
安裝 Rootless Mode
安裝 Rootless Mode 需要下載 Docker 提供的安裝指令碼,並仔細檢查指令碼內容以確保安全性。
$ diff -y get-docker.sh install.sh
詳細步驟與程式碼解說
下載安裝指令碼:從 Docker 官方提供的 URL 下載安裝指令碼。
$ curl -fsSL https://get.docker.com/rootless -o get-docker.sh內容解密:
curl命令用於從指定 URL 下載檔案。-fsSL引數確保curl在遇到錯誤時會失敗,並且顯示下載進度。-o get-docker.sh將下載的內容儲存到名為get-docker.sh的檔案中。
檢查指令碼內容:對比下載的指令碼和 Docker GitHub 上的原始碼,以確保指令碼的安全性。
$ diff -y get-docker.sh install.sh內容解密:
diff命令用於比較兩個檔案的差異。-y引數以並排的方式顯示比較結果,便於檢查差異。
執行安裝指令碼:確認指令碼內容無誤後,以非特權使用者身份執行安裝指令碼。
$ sh get-docker.sh內容解密:
sh命令用於執行 shell 指令碼。- 指令碼將自動完成 Rootless Mode 的安裝和組態。
無根容器執行環境的安裝與設定
Docker 的無根容器執行環境(Rootless Mode)是一項新興功能,讓使用者能夠在非 root 許可權下執行 Docker 容器。以下將詳細介紹如何安裝與設定無根容器執行環境。
安裝無根容器執行環境
首先,切換到非 root 使用者,例如 chris。請根據您的實際使用者名稱進行調整。
$ sudo -i chris
接著,下載 Docker 的無根容器執行環境安裝指令碼。
$ curl https://get.docker.com/rootless > install.sh
使安裝指令碼具有執行許可權並執行它。
$ chmod +x install.sh ; ./install.sh
安裝完成後,您會看到 Docker Engine 的客戶端和伺服器版本資訊。
設定環境變數
根據安裝指令碼的輸出提示,設定必要的環境變數。
$ export XDG_RUNTIME_DIR=/home/$USER/rootless
$ export PATH=/home/$USER/bin:$PATH
$ export DOCKER_HOST=unix:///home/$USER/rootless/docker.sock
建立一個目錄來儲存容器內容和設定。
$ mkdir rootless
啟動無根容器執行環境
使用以下命令啟動無根容器執行環境,並指定 overlay2 儲存驅動程式。
$ bin/dockerd-rootless.sh --experimental --storage-driver overlay2
內容解密:
bin/dockerd-rootless.sh:啟動無根容器執行環境的指令碼。--experimental:啟用實驗性功能。--storage-driver overlay2:指定使用overlay2儲存驅動程式。
驗證無根容器執行環境
開啟另一個終端,使用以下命令啟動一個無根 Apache 容器。
$ systemctl --user start docker
$ export XDG_RUNTIME_DIR=/home/chris/rootless; \
export DOCKER_HOST=unix:///home/chris/rootless/docker.sock; \
export PATH=/home/chris/bin:$PATH
$ docker run -d -p 8000:80 httpd
內容解密:
systemctl --user start docker:啟動 Docker 服務。docker run -d -p 8000:80 httpd:下載httpd映像並啟動一個容器,將容器的 80 連線埠對映到主機的 8000 連線埠。
疑難排解
如果遇到問題,可以使用以下命令停止相關程式。
$ pkill rootlesskit; pkill dockerd; pkill experimental; pkill containerd
內容解密:
pkill:根據名稱殺死程式。rootlesskit、dockerd、experimental、containerd:與 Docker 無根容器執行環境相關的程式名稱。
綜上所述,無根容器執行環境提供了一種更安全的方式來執行 Docker 容器。儘管目前仍處於實驗階段,但它已經展現出巨大的潛力。未來,隨著功能的完善和穩定性的提高,無根容器執行環境有望成為 Docker 使用者的首選。
無根容器執行環境的安全性提升
在前一章節中,我們成功地在無根模式下安裝並執行了Docker容器,並且驗證了其網路服務的可存取性。下面我們將繼續探討另一種無根容器執行環境——Podman。
無根模式下的Docker網路問題
首先,回顧一下在無根模式下執行Docker容器時遇到的網路問題。由於網路名稱空間(network namespace)在無根模式下的運作方式不同,直接使用容器的IP地址無法存取其暴露的埠。
$ docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS
a8a031f6a3a3 httpd "httpd-foreground" Up 15 minutes 0.0.0.0:8000->80/tcp
$ docker inspect a8a031f6a3a3 | grep IPAddress
"IPAddress": "172.17.0.2",
"MacAddress": "02:42:ac:11:00:02",
"IPAddress": "172.17.0.2",
$ nc -v 172.17.0.2 8000
內容解密:
docker ps用於列出正在執行的容器,確認Apache的httpd容器正在執行。docker inspect用於檢查容器的詳細資訊,包括其IP地址。nc -v(netcat)用於測試與容器IP地址的連線,但由於無根模式下的網路名稱空間限制,連線失敗。
然而,透過主機的網路堆疊(localhost)存取暴露的埠則是可行的:
$ nc -v localhost 8000
Connection to localhost 8000 port [tcp/*] succeeded!
$ curl localhost:8000
<html><body><h1>It works!</h1></body></html>
內容解密:
- 使用
nc -v localhost 8000成功連線到暴露的埠,證明可以透過主機的網路堆疊存取容器服務。 curl localhost:8000傳回預期的HTML內容,進一步確認了連線的正確性。
執行無根Podman
接下來,我們探討Podman這一容器執行環境。Podman是一種能夠在無根模式下執行的容器執行環境,並且具備一些Docker所不具備的特點。
Podman的特點與優勢
- 無守護程式(Daemonless):Podman不需要一直執行的守護程式,這降低了安全風險。
- 相容性:Podman相容Docker映象,並且支援Open Container Initiative(OCI)標準。
- Red Hat的支援:Red Hat已經將Podman整合到Red Hat Enterprise Linux v8.0中,甚至提供了
podman-docker套件來簡化從Docker到Podman的遷移。
# 示例命令:使用Podman執行容器
$ podman run -d --name myhttpd -p 8000:80 httpd
內容解密:
podman run用於執行一個新的容器。-d表示以分離模式(後台)執行容器。--name myhttpd指定容器的名稱為myhttpd。-p 8000:80將主機的8000埠對映到容器的80埠。httpd指定要執行的Docker映象名稱。
Podman:無需守護程式的容器執行時
Podman是一種容器執行時,與Docker類別似,但它不需要守護程式(daemon)來執行容器。Podman使用一個名為conmon的小程式來監控容器的父程式,並在容器停止或失敗時傳遞離開程式碼。
Podman的設計優勢
Podman的設計具有以下優勢:
- 無守護程式:與Docker不同,Podman不需要一個一直執行的守護程式來管理容器。這使得Podman更加輕量和安全。
conmon監控:conmon是一個用C語言編寫的小程式,負責監控容器的父程式,並在容器停止或失敗時傳遞離開程式碼。
安裝Podman
要在Ubuntu 20.04 LTS上安裝Podman,請按照以下步驟進行:
新增軟體源:首先,將Podman的軟體源新增到
apt套件管理員中。執行以下命令:$ echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_20.04/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list如果您使用的不是Ubuntu 20.04,請相應地修改命令中的版本號。
新增軟體源金鑰:接下來,將軟體源的金鑰新增到您的金鑰環中,以確保軟體源的可信度。執行以下命令:
$ curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_20.04/Release.key | sudo apt-key add -成功後,您應該會看到「OK」的回應。
更新和升級套件:更新您的套件列表,並升級任何需要升級的套件:
$ sudo apt-get update; sudo apt-get upgrade -y安裝Podman:現在,您可以安裝Podman了:
$ apt-get -y install podman注意命令的輸出,瞭解安裝了哪些相關套件。
驗證安裝:檢查Podman是否正確安裝:
$ podman -v podman version 2.0.4
組態無根容器
要執行無根容器,需要檢查和組態UID和GID對映設定檔案。
檢查UID和GID對映:執行以下命令,檢查
/etc/subuid和/etc/subgid檔案是否為空:$ cat /etc/subuid $ cat /etc/subgid如果檔案中有內容,可以在開發系統上刪除這些內容。
建立使用者(可選):您可以建立一個特定的使用者來執行無根容器。例如:
$ sudo adduser poduser本例中,我們繼續使用使用者
chris。組態UID和GID範圍:使用以下命令為使用者
chris設定UID和GID範圍:$ sudo usermod --add-subuids 200000-201000 --add-subgids 200000-201000 chris
執行Podman無根模式
檢查Podman程式:由於Podman不是以守護程式模式執行,您可以使用以下命令檢查是否有Podman相關程式在執行:
$ ps -ef | grep podman通常,您只會看到剛剛執行的
grep命令。執行容器:使用以下命令執行一個Apache容器:
$ podman run -it -p 8000:80 httpd:latest由於沒有使用
-d引數,容器的STDOUT日誌將直接輸出到終端。
內容解密:
此命令的作用是使用Podman執行一個Apache容器,並將容器的80埠對映到主機的8000埠。-it引數允許與容器進行互動式操作。
- 檢查容器狀態:在另一個終端中,使用以下命令檢查容器的狀態:
$ podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e09883662c2b docker.io/library/httpd:latest httpd-foreground
內容解密:
此命令用於列出當前執行的容器,包括容器的ID、使用的映象、執行的命令、建立時間、狀態、埠對映和名稱。
- 測試網路連線:使用以下命令測試主機與容器之間的網路連線:
$ curl localhost:8000 <html><body><h1>It works!</h1></body></html>
內容解密:
此命令向主機的8000埠傳送一個HTTP請求,由於容器的80埠對映到主機的8000埠,因此請求被轉發到容器的Apache伺服器,並傳回預期的回應。
Rootless Runtimes 的進階探討
在探討容器安全性的過程中,Rootless Runtimes 的概念逐漸受到重視。Rootless Runtimes 允許使用者在不依賴 root 使用者的情況下執行容器化工作負載。本章節將深入分析 Docker Engine 和 Podman 兩種容器執行時在 Rootless 模式下的差異和組態方法。
Docker 與 Podman 的 Rootless 模式比較
Docker Engine 在預設情況下需要 root 許可權來執行容器。然而,透過特定的組態,可以實作 Rootless 模式。相較之下,Podman 設計之初就考慮到了 Rootless 模式的需求,因此在安裝和組態上更加簡便。
Docker Engine 的 Rootless 模式
Docker Engine 的 Rootless 模式需要進行額外的組態。首先,使用者需要確保系統支援並啟用相關功能。以下是一個範例程式碼,用於檢查 Docker 是否以 Rootless 模式執行:
$ docker run -d -p 8000:8000 nginx
$ curl http://localhost:8000
Podman 的 Rootless 模式
Podman 在預設情況下不分配 IP 位址給容器。以下範例展示瞭如何檢查容器的 IP 位址:
$ podman ps
$ podman inspect e098 | grep IP
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0
內容解密:
此段程式碼首先列出正在執行的容器,接著檢查指定容器的 IP 組態。輸出結果顯示,Podman 在預設情況下不分配 IP 位址。實際上,Podman 使用 slirp4netns 提供使用者空間網路功能,使容器能夠與主機和其他外部網路進行通訊。
組態檔解析
Podman 的組態檔存放在 /usr/share/containers 和 /etc/containers 目錄下。這些組態檔包括 containers.conf、seccomp.json 和 registries.conf 等。
containers.conf 組態檔
containers.conf 組態檔允許使用者調整容器的預設行為,包括 cgroups 和資源配額的設定。
# List of default capabilities for containers.
# If it is empty or commented out,
# the default capabilities defined in the container engine will
# be added.
default_capabilities = [
"AUDIT_WRITE",
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"FSETID",
"KILL",
"MKNOD",
"NET_BIND_SERVICE",
"NET_RAW",
"SETGID",
"SETPCAP",
"SETUID",
"SYS_CHROOT",
]
內容解密:
此組態段落定義了容器的預設能力。若該列表為空或被註解,則使用容器引擎的預設能力。
容器執行階段的安全性防護
為了確保容器的安全性,除了正確組態許可權外,還需要監控和自動修復異常行為。Falco 是一款優秀的開源工具,能夠強制容器按照預期行為執行,並與 Kubernetes API 稽核事件整合,提供額外的安全性。
# 安裝 Falco
$ curl -s https://falco.org/install.sh | sudo bash
內容解密:
此命令用於安裝 Falco。Falco 能夠根據自定義的規則集監控和警示容器的異常行為,從而提高容器的安全性。