在容器化應用日漸普及的今天,容器儲存安全的重要性不容忽視。Podman 作為新一代容器管理工具,提供了豐富的儲存管理功能,讓我們能更精細地控制容器的儲存環境。本文將深入 SELinux、tmpfs 和映像掛載等關鍵技術,並結合實際案例,分享玄貓在容器儲存安全方面的實戰經驗。在實際佈署容器化應用時,除了確保應用程式本身的安全性外,儲存安全也是一個至關重要的環節。Podman 提供了多種機制來保護容器儲存的安全性,例如 SELinux、tmpfs 和映像掛載。善用這些機制,可以有效提升容器化應用的整體安全性。透過 SELinux 的 MCS 機制,我們可以對容器的儲存資源進行更細粒度的控制,有效防止惡意容器竊取或篡改其他容器的資料。tmpfs 則提供了一種高效能的記憶體檔案系統,適用於存放臨時資料,提升容器的 I/O 效能。映像掛載則允許我們將容器映像作為唯讀檔案系統掛載到容器中,方便進行除錯和問題排查。
理解這些技術的原理和使用方法,對於構建安全的容器化環境至關重要。除了技術層面的安全措施外,良好的安全實踐也同樣重要。例如,定期更新容器映像,避免使用來路不明的映像,以及對容器的儲存資源進行定期的安全稽核等。
玄貓解密:容器儲存的奧秘 - SELinux、tmpfs 與映像掛載
1. 容器儲存安全:SELinux 的多重防護
在容器技術中,儲存的安全性至關重要。預設情況下,容器掛載的主機儲存可能存在安全風險,因為所有具有相同安全敏感度(s0)的行程都可以讀寫該資源。這意味著,同一主機上的惡意容器可能透過竊取或覆寫資料來攻擊其他容器。
1.1 MCS:為容器儲存加上多重鎖
為瞭解決這個問題,SELinux 引入了多類別安全性(MCS)。MCS 透過額外的類別標籤來限制資源的存取。這些標籤與其他 SELinux 標籤一起應用於資源,只有具有相同類別的行程才能存取。
當容器啟動時,其中的行程會被標記上 MCS 類別,例如 cXXX,cYYY,其中 XXX 和 YYY 是隨機產生的整數。Podman 能夠自動將 MCS 類別應用於掛載的資源,只需使用 :Z 字尾即可。
1.2 實戰演練:使用 :Z 安全掛載
讓我們再次執行 Nginx 容器,這次加上 :Z 字尾:
$ podman run -d \
--name custom_nginx \
–p 8080:80 \
-v ~/custom_docroot:/usr/share/nginx/html:Z \
docker.io/library/nginx
透過 ls -alZ ~/custom_docroot 命令,我們可以觀察到掛載的資料夾已經被重新標記了 MCS 類別。
1.3 意料之外的 403:MCS 的威力展現
如果我們再次執行第二個容器,並使用相同的 :Z 掛載相同的目錄,會發生什麼事呢?
$ podman run -d \
--name custom_nginx2 \
–p 8081:80 \
-v ~/custom_docroot:/usr/share/nginx/html:Z \
docker.io/library/nginx
此時,第一個容器會出現 403 Forbidden 的錯誤,因為第二個容器使用 :Z 字尾重新標記了目錄,導致第一個容器無法存取。
玄貓提醒: 在使用 bind mount 時要特別小心,避免意外重新標記系統或 home 目錄。
2. tmpfs:容器的記憶體儲存空間
有時候,我們需要為容器提供非永續性的儲存空間,例如用於快取。在這種情況下,使用 tmpfs 是一個理想的選擇。
2.1 tmpfs 的優勢:速度與隔離
tmpfs 是一種虛擬記憶體檔案系統,其所有內容都儲存在主機的虛擬記憶體中。tmpfs 的優點是 I/O 速度快,因為讀寫操作主要在 RAM 中進行。
2.2 使用 --mount 掛載 tmpfs
可以使用 --mount 選項將 tmpfs 卷掛載到容器。這個選項更加詳細,可以指定儲存型別、來源、目的地和額外的掛載選項。
$ podman run –d –p 8080:80 \
--name tmpfs_example1 \
--mount type=tmpfs,tmpfs-size=512M,destination=/tmp \
docker.io/library/httpd
上述命令建立了一個 512 MB 的 tmpfs 卷,並將其掛載到容器的 /tmp 目錄。
2.3 使用 --tmpfs 簡化掛載
--tmpfs 選項提供了一種更簡潔的方式來掛載 tmpfs 卷:
$ podman run –d –p 8080:80 \
--name tmpfs_example2 \
--tmpfs /tmp:rw,size= 524288k,mode=1777 \
docker.io/library/httpd
這個例子與前一個例子效果相同,但使用了更簡短的語法。
玄貓觀察: tmpfs 還有一個有趣的特性,就是 SELinux 的自動 MCS 標記,這可以自動隔離檔案系統,防止其他容器存取記憶體中的資料。
3. 映像掛載:容器的擴充工具箱
OCI 映像不僅可以作為容器的基礎,還可以在執行時掛載到容器的檔案系統中。這對於故障排除或將外部映像中的二進位檔案附加到容器非常有用。
3.1 映像掛載的特性:唯讀保護
當 OCI 映像掛載到容器中時,會建立一個額外的覆寫層。即使映像以讀寫許可權掛載,使用者也永遠不會修改原始映像,而只會修改上層的覆寫層。
3.2 實戰演練:掛載 busybox 映像
以下範例將 busybox 映像以讀寫許可權掛載到 Alpine 容器中:
$ podman run -it \
--mount type=image,src=docker.io/library/busybox,dst=/
mnt,rw=true \
alpine
玄貓提醒: 掛載的映像必須已經快取在主機上。Podman 只會在建立容器時提取基礎容器映像,但它期望掛載的映像已經可用。
容器儲存與 Podman 功能:資料管理的實戰解析
在容器技術中,儲存管理至關重要。它不僅影響容器的效能,還關乎資料的永續性和安全性。本文將探討 Podman 提供的儲存功能,以及如何在單主機和多主機環境中有效管理容器資料。
容器儲存的重要性
容器的儲存管理需要在單主機和多主機環境中正確管理。在單主機環境中,儲存管理主要關注如何有效地利用本地資源,確保容器可以快速讀寫資料。在多主機環境中,則需要考慮資料的一致性和分享,這通常涉及到網路儲存和分散式檔案系統。
深入探索容器儲存特性與驅動程式
Podman 支援多種儲存驅動程式,其中 overlayfs 是最常用的之一。Overlayfs 是一種聯合檔案系統,它允許多個目錄疊加在一起,形成一個單一的虛擬檔案系統。這種技術非常適合容器,因為它可以實作層疊式的映象結構,提高儲存效率和映象分享能力。
容器檔案的複製與提交
在容器操作中,經常需要在容器和主機之間複製檔案。Podman 提供了簡單易用的命令,如 podman cp,可以方便地實作檔案複製。此外,對容器所做的變更可以提交為新的映象,這有助於建立自定義的容器映象。
儲存掛載的各種場景
Podman 支援多種儲存掛載方式,包括繫結掛載(bind mounts)、卷(volumes)、tmpfs、映象和 devpts。繫結掛載允許將主機的檔案或目錄掛載到容器中,卷則是由 Podman 管理的持久化儲存,tmpfs 是一種根據記憶體的檔案系統,速度非常快,適合儲存臨時資料。
SELinux 與儲存隔離
SELinux 在容器儲存管理中扮演重要角色。它可以透過安全策略,隔離不同容器的儲存資源,防止容器之間的互相干擾。透過正確組態 SELinux,可以大大提高容器的安全性。
Attaching devpts:連線虛擬終端
devpts 選項用於將虛擬終端(PTS)連線到容器。這個功能在 Podman 2.1.0 中引入,主要用於支援需要將主機的 /dev 目錄掛載到容器中,同時還需要建立終端的情境。
要建立一個具有 /dev 檔案系統和連線的 devpts 裝置的容器,可以執行以下命令:
$ sudo podman run -it \
-v /dev/:/dev:rslave \
--mount type=devpts,destination=/dev/pts \
docker.io/library/fedora
這個命令會建立一個新的 Fedora 容器,並將主機的 /dev 目錄以 rslave 模式掛載到容器中,同時將 devpts 裝置掛載到 /dev/pts。
為了檢查掛載選項的結果,我們需要在容器內部安裝一個額外的工具:
[root@034c8a61a4fc /]# dnf install -y toolbox
安裝完成後,我們可以使用 mount 命令來檢查掛載點:
# mount | grep '\/dev\/pts'
devpts on /dev/pts type devpts
(rw,nosuid,noexec,relatime,seclabel,gid=5,mode=620,ptmxmode=000)
devpts on /dev/pts type devpts
(rw,nosuid,noexec,relatime,context="system_u:object_r:container_file_t:s0:c299,c741",gid=5,mode=620,ptmxmode=666)
這個輸出顯示,容器內部有一個額外的、非隔離的 devpts 裝置掛載在 /dev/pts 上。
在為某金融科技公司設計容器化解決方案時,玄貓發現 devpts 選項對於需要存取主機裝置的容器非常有用。透過這種方式,容器可以在保持隔離性的同時,也能夠使用主機的硬體資源。
使用 Buildah 從頭開始建構容器
Buildah 是一個專門用於建構容器映象的工具。與 Podman 相比,Buildah 更關注映象的建構過程,提供了更多的靈活性和控制力。
Buildah 的基本概念
Buildah 的核心概念是 “working container”,這是一個可讀寫的容器例項,可以用於修改和建立新的映象層。Buildah 允許使用者從 scratch(完全空白)開始建構映象,或者根據現有的映象進行修改。
準備建構環境
在使用 Buildah 之前,需要確保系統上已經安裝了 Buildah。玄貓建議使用 Fedora 或 CentOS 等 Linux 發行版,因為這些系統對容器技術的支援較好。
選擇建構策略
建構容器映象有多種策略可選。一種常見的方法是使用 Dockerfile 或 Containerfile,這是一種宣告式的建構方式,透過定義一系列的指令來描述映象的內容。另一種方法是使用 Buildah 的 API,透過程式碼來控制映象的建構過程。
從 scratch 開始建構映象
從 scratch 開始建構映象意味著從一個完全空白的基礎開始,逐步增加所需的檔案和組態。這種方法可以建立非常精簡的映象,但需要更多的手動組態。
從 Dockerfile 建構映象
Dockerfile 是一種常用的映象建構方式。它使用簡單的語法,描述了映象的建構步驟。Buildah 可以直接使用 Dockerfile 來建構映象,這使得現有的 Dockerfile 可以輕鬆地遷移到 Buildah。
在為某電商平台匯入容器技術時,玄貓發現 Buildah 在建構自定義映象方面非常強大。透過 Buildah,可以精確控制映象的每一層,從而建立出高效、安全的容器環境。
玄貓對容器技術的洞察
容器技術已經成為現代軟體開發和佈署的根本。Podman 和 Buildah 等工具的出現,使得容器的管理和建構變得更加容易。然而,要充分利用容器技術的優勢,需要深入理解其底層原理,並掌握各種儲存和安全技術。
從玄貓的經驗來看,容器技術將更加註重安全性、可移植性和自動化。隨著容器技術的不斷演進,我們可以期待更多的創新和突破,為軟體開發和佈署帶來更大的便利。
運用 Podman 開發客製化容器映像檔:從基礎到實踐
在容器化的世界中,映像檔是應用程式執行的根本。Podman 作為一個強大的容器管理工具,讓我們能夠輕鬆地建立、管理和執行容器。本文將探討如何使用 Podman 構建映像檔,重點介紹 Dockerfile 的語法和常用指令,並分享一些實用的技巧和最佳實踐。
Dockerfile:容器映像檔的藍圖
Dockerfile 是一個包含一系列指令的文字檔,這些指令描述瞭如何構建一個容器映像檔。構建工具會按照 Dockerfile 中指令的順序逐一執行,最終生成一個包含所需應用程式和依賴項的映像檔。
以下是一個簡單的 Dockerfile 範例:
FROM docker.io/library/fedora
RUN dnf install -y httpd && dnf clean all -y
COPY index.html /var/www/html
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]
這個 Dockerfile 包含了四個基本指令:
- FROM: 指定基礎映像檔,作為構建過程的起點。
- RUN: 在構建過程中執行命令,例如安裝軟體包。
- COPY: 將檔案或目錄從構建工作目錄複製到映像檔中。
- CMD: 定義容器啟動時執行的命令。
映像檔層疊與快取機制
當執行 RUN 和 COPY 等指令時,Podman 會建立新的層 (layer),這些層包含了對檔案系統的修改。這些層會被快取,以便在後續的構建過程中重複使用,從而提高構建效率。
所有中間容器會產生唯讀層,並由 overlay graph driver 合併。使用者無需手動管理快取層,Podman 會自動處理。透過重複相同的邏輯,Podman 會在基礎映像檔的層之上建立新的映像檔。
你可以將映像檔層合併為單一層,以避免對 overlay 效能產生負面影響。Podman 提供了相同的功能,讓你可以選擇是否快取中間層。
並非所有 Dockerfile 指令都會變更檔案系統,只有那些會變更檔案系統的指令才會建立新的映像檔層。所有其他指令(例如範例中的 CMD 指令)只會產生一個包含中繼資料的空層,而不會變更 overlay 檔案系統。
最佳實踐:減少映像檔層的數量
一般來說,只有 RUN、COPY 和 ADD 指令會透過有效變更檔案系統來建立新的層。Dockerfile 或 Containerfile 中的所有其他指令只會建立暫時的中間映像檔,而不會影響最終的映像檔檔案系統。
玄貓建議限制 Dockerfile 中 RUN、COPY 和 ADD 指令的數量,因為映像檔中包含過多的層並不是一個好的模式,而與會影響 graph driver 的效能。
檢視映像檔歷史
我們可以檢視映像檔的歷史,以及已套用至每個層的操作。以下範例顯示 podman inspect 命令的輸出摘錄,目標映像檔是從先前的範例 Dockerfile 建立的潛在映像檔:
$ podman inspect myhttpd
[...omitted output]
"History": [
{
"created": "2021-04-01T17:59:37.09884046Z",
"created_by": "/bin/sh -c #(nop) LABEL
maintainer=Clement Verna 
\u003ccverna@fedoraproject.org\
u003e 
",
"empty_layer": true
},
{
"created": "2021-04-01T18:00:19.741002882Z",
"created_by": "/bin/sh -c #(nop) ENV
DISTTAG=f34container FGC=f34 FBR=f34",
"empty_layer": true
},
{
"created": "2021-07-23T11:16:05.060688497Z",
"created_by": "/bin/sh -c #(nop) ADD file:85d7
f2d8e4f31d81b27b8e18dfc5687b5dabfaafdb2408a3059e120e4c15307b in
/ "
},
{
"created": "2021-07-23T11:16:05.833115975Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/
bash\"]",
"empty_layer": true
},
{
"created": "2021-10-24T21:27:18.783034844Z",
"created_by": "/bin/sh -c dnf install -y httpd
\u0026\u0026 dnf clean all -y ",
"comment": "FROM docker.io/library/
fedora:latest"
},
{
"created": "2021-10-24T21:27:21.095937071Z",
"created_by": "/bin/sh -c #(nop) COPY file:
78c6e1dcd6f819581b54094fd38a3fd8f170a2cb768101e533c964e
04aacab2e in /var/www/html "
},
{
"created": "2021-10-24T21:27:21.182063974Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/usr/
sbin/httpd\", \"-DFOREGROUND\"]",
"empty_layer": true
}
]
[...omitted output]
檢視映像檔歷史記錄的最後三個專案,我們可以注意到 Dockerfile 中定義的確切指令,包括最後一個 CMD 指令,該指令不會建立任何新層,而是建立將保留在映像檔組態中的中繼資料。
Dockerfile 指令詳解
Dockerfile 和 Containerfile 分享相同的語法。這些檔案中的指令應被視為傳遞至容器引擎或建置工具的命令。以下概述最常用的指令:
- 
FROM: 建置階段的第一個指令,定義用作建置起點的基礎映像檔。它遵循 FROM <image>[:<tag>]語法來識別要使用的正確映像檔。
- 
RUN: 此指令會指示引擎在暫時容器內執行作為引數傳遞的命令。它遵循 RUN <command>語法。呼叫的二進位檔或指令碼必須存在於基礎映像檔或先前的層中。如前所述,RUN指令會建立新的映像檔層;因此,常見的做法是將命令串連到相同的RUN指令中,以避免過多的層。以下範例將三個命令壓縮到相同的 RUN指令中:RUN dnf upgrade -y && \ dnf install httpd -y && \ dnf clean all -y
- 
COPY: 此指令會將檔案和資料夾從建置工作目錄複製到建置沙箱。複製的資源會保留在最終映像檔中。它遵循 COPY <src>... <dest>語法,而與它有一個非常有用的選項,可讓我們定義目的地使用者和群組,而不是稍後手動變更擁有權--chown=<user>:<group>。
- 
ADD: 此指令會將檔案、資料夾和遠端 URL 複製到建置目的地目標。它遵循 ADD <src>... <dest>語法。此指令也支援將 tar 檔案從來源直接自動解壓縮到目標路徑中。
- 
ENTRYPOINT: 容器中執行的命令。它會從命令列 (以 podman run <image> <arguments>形式) 或從 CMD 指令接收引數。ENTRYPOINT 映像檔無法被命令列引數覆寫。支援的形式如下:- ENTRYPOINT ["command", "param1", "paramN"](也稱為 exec 形式)
- ENTRYPOINT command param1 paramN(shell 形式)
 
容器映像檔建構基礎:Podman 實戰
在使用容器技術時,映像檔的建構是不可或缺的一環。Podman 作為一個強大的容器管理工具,不僅能執行和管理容器,還能以 rootless 模式建構容器映像檔,這在安全性上是一大優勢。本文將探討如何使用 Podman 建構映像檔,並分享一些實戰技巧。
Dockerfile 指令詳解:開發客製化映像檔的根本
Dockerfile 是一個文字檔,其中包含一系列指令,用於自動化建構容器映像檔的過程。理解這些指令的用途和用法,是開發客製化映像檔的基礎。以下是一些常用的 Dockerfile 指令:
- FROM: 指定基礎映像檔,後續的指令將在此基礎上執行。例如,FROM docker.io/library/fedora表示使用 Fedora 作為基礎映像檔。
- RUN: 在映像檔中執行命令。例如,RUN dnf install -y httpd && dnf clean all -y會安裝 Apache HTTP Server 並清理系統。
- CMD: 設定容器啟動時執行的預設命令。例如,CMD ["/usr/sbin/httpd", "-DFOREGROUND"]會啟動 Apache HTTP Server。
- ENTRYPOINT: 設定容器的進入點,通常是一個指令碼或二進位檔案。與 CMD不同,ENTRYPOINT的命令不會被docker run提供的命令覆寫。
- LABEL: 為映像檔增加中繼資料標籤,例如版本號、作者等。LABEL version="1.0" maintainer="BlackCat"
- EXPOSE: 宣告容器將監聽的連線埠。例如,EXPOSE 80/tcp宣告容器將監聽 TCP 80 連線埠。
- ENV: 設定環境變數,這些變數在建構時和執行時都可用。例如,ENV APP_HOME=/opt/app設定APP_HOME環境變數。
- VOLUME: 建立一個卷宗,用於在容器和主機之間分享資料。例如,VOLUME ["/path/to/dir"]建立一個卷宗。關於卷宗的更多細節,請參考第五章。
- USER: 指定後續 RUN、CMD和ENTRYPOINT指令的使用者名稱和使用者組。例如,USER <username>:[<groupname>]。
- WORKDIR: 設定建構過程中的工作目錄,這個值在容器執行時也會被保留。例如,WORKDIR /path/to/workdir。
- ONBUILD: 定義一個觸發命令,在映像檔被用作另一個映像檔的父映像檔時執行。例如,ONBUILD ADD . /opt/app。
Podman 建構映像檔:告別 Docker 的學習曲線
如果你熟悉 Docker,那麼使用 Podman 建構映像檔將會非常容易,因為它們的命令和語法幾乎相同。Podman 的一個顯著優勢是它可以在 rootless 模式下建構容器,這提高了安全性。
以下是一個使用 podman build 命令建構映像檔的例子:
$ podman build -t myhttpd .
STEP 1/4: FROM docker.io/library/fedora
STEP 2/4: RUN dnf install -y httpd && dnf clean all -y
[...omitted output]
--> 50a981094eb
STEP 3/4: COPY index.html /var/www/html
--> 73f8702c5e0
STEP 4/4: CMD ["/usr/sbin/httpd", "-DFOREGROUND"]
COMMIT myhttpd
--> e773bfee6f2
Successfully tagged localhost/myhttpd:latest
e773bfee6f289012b37285a9e559bc44962de3aeed001455231b5a8f2721b8f9
這個命令會依序執行 Dockerfile 中的指令,並將中間層持久化,直到最終映像檔被提交和標記。
客製化 Web 伺服器:Dockerfile 例項
以下是一個客製化 Web 伺服器的 Dockerfile 範例:
FROM docker.io/library/fedora
# 安裝所需套件
RUN set -euo pipefail; \
    dnf upgrade -y; \
    dnf install httpd -y; \
    dnf clean all -y; \
    rm -rf /var/cache/dnf/*
# 為 rootless 執行客製化 webserver 設定
RUN set -euo pipefail; \
    sed -i 's|Listen 80|Listen 8080|' \
    /etc/httpd/conf/httpd.conf; \
玄貓解密
- FROM docker.io/library/fedora: 指定使用 Fedora 映像檔作為基礎。
- RUN set -euo pipefail;: 設定 shell 選項,確保指令碼在遇到錯誤時立即離開。
- dnf upgrade -y;: 升級所有已安裝的套件。
- dnf install httpd -y;: 安裝 Apache HTTP Server。
- dnf clean all -y;: 清理 DNF 快取。
- rm -rf /var/cache/dnf/*: 移除 DNF 快取目錄。
- sed -i 's|Listen 80|Listen 8080|' /etc/httpd/conf/httpd.conf;: 將 Apache 監聽連線埠從 80 修改為 8080,這在 rootless 模式下非常有用,因為普通使用者無法監聽 1024 以下的連線埠。
在容器映像檔建構的旅程中,掌握 Dockerfile 指令和善用 Podman 的 rootless 建構模式,能幫助我們開發安全、高效與客製化的容器映像檔。
 
            