返回文章列表

Rootless 容器執行環境安全強化

本文探討 Rootless 容器執行環境的安全性,比較 Docker 和 Podman 在 Rootless 模式下的差異,並提供相關組態方法。Rootless 模式允許使用者在非 root 許可權下執行容器,提升安全性。文章涵蓋 Docker Rootless 模式的原理、限制和安裝步驟,以及 Podman

容器技術 系統安全

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 之前,需要確保系統滿足特定的組態要求:

  1. 安裝 uidmap 套件:在 Debian 系列的系統上,需要安裝 uidmap 套件來支援使用者名稱空間的對映。

    $ apt install uidmap
    
  2. 檢查 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

詳細步驟與程式碼解說

  1. 下載安裝指令碼:從 Docker 官方提供的 URL 下載安裝指令碼。

    $ curl -fsSL https://get.docker.com/rootless -o get-docker.sh
    

    內容解密:

    • curl 命令用於從指定 URL 下載檔案。
    • -fsSL 引數確保 curl 在遇到錯誤時會失敗,並且顯示下載進度。
    • -o get-docker.sh 將下載的內容儲存到名為 get-docker.sh 的檔案中。
  2. 檢查指令碼內容:對比下載的指令碼和 Docker GitHub 上的原始碼,以確保指令碼的安全性。

    $ diff -y get-docker.sh install.sh
    

    內容解密:

    • diff 命令用於比較兩個檔案的差異。
    • -y 引數以並排的方式顯示比較結果,便於檢查差異。
  3. 執行安裝指令碼:確認指令碼內容無誤後,以非特權使用者身份執行安裝指令碼。

    $ 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:根據名稱殺死程式。
  • rootlesskitdockerdexperimentalcontainerd:與 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的特點與優勢

  1. 無守護程式(Daemonless):Podman不需要一直執行的守護程式,這降低了安全風險。
  2. 相容性:Podman相容Docker映象,並且支援Open Container Initiative(OCI)標準。
  3. 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,請按照以下步驟進行:

  1. 新增軟體源:首先,將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,請相應地修改命令中的版本號。

  2. 新增軟體源金鑰:接下來,將軟體源的金鑰新增到您的金鑰環中,以確保軟體源的可信度。執行以下命令:

    $ curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_20.04/Release.key | sudo apt-key add -
    

    成功後,您應該會看到「OK」的回應。

  3. 更新和升級套件:更新您的套件列表,並升級任何需要升級的套件:

    $ sudo apt-get update; sudo apt-get upgrade -y
    
  4. 安裝Podman:現在,您可以安裝Podman了:

    $ apt-get -y install podman
    

    注意命令的輸出,瞭解安裝了哪些相關套件。

  5. 驗證安裝:檢查Podman是否正確安裝:

    $ podman -v
    podman version 2.0.4
    

組態無根容器

要執行無根容器,需要檢查和組態UID和GID對映設定檔案。

  1. 檢查UID和GID對映:執行以下命令,檢查/etc/subuid/etc/subgid檔案是否為空:

    $ cat /etc/subuid
    $ cat /etc/subgid
    

    如果檔案中有內容,可以在開發系統上刪除這些內容。

  2. 建立使用者(可選):您可以建立一個特定的使用者來執行無根容器。例如:

    $ sudo adduser poduser
    

    本例中,我們繼續使用使用者chris

  3. 組態UID和GID範圍:使用以下命令為使用者chris設定UID和GID範圍:

    $ sudo usermod --add-subuids 200000-201000 --add-subgids 200000-201000 chris
    

執行Podman無根模式

  1. 檢查Podman程式:由於Podman不是以守護程式模式執行,您可以使用以下命令檢查是否有Podman相關程式在執行:

    $ ps -ef | grep podman
    

    通常,您只會看到剛剛執行的grep命令。

  2. 執行容器:使用以下命令執行一個Apache容器:

    $ podman run -it -p 8000:80 httpd:latest
    

    由於沒有使用-d引數,容器的STDOUT日誌將直接輸出到終端。

內容解密:

此命令的作用是使用Podman執行一個Apache容器,並將容器的80埠對映到主機的8000埠。-it引數允許與容器進行互動式操作。

  1. 檢查容器狀態:在另一個終端中,使用以下命令檢查容器的狀態:
    $ podman ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    e09883662c2b docker.io/library/httpd:latest httpd-foreground 
    

內容解密:

此命令用於列出當前執行的容器,包括容器的ID、使用的映象、執行的命令、建立時間、狀態、埠對映和名稱。

  1. 測試網路連線:使用以下命令測試主機與容器之間的網路連線:
    $ 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.confseccomp.jsonregistries.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 能夠根據自定義的規則集監控和警示容器的異常行為,從而提高容器的安全性。