在雲端原生時代,容器技術已成為應用程式佈署和管理的關鍵。理解容器與虛擬機器的差異,以及容器映像的安全性,對於構建安全可靠的系統至關重要。本文將深入探討這些議題,並提供實務上的建議。
容器化技術的核心在於輕量級的隔離環境,讓應用程式能在幾乎原生的效能下執行,同時又能享有資源隔離的好處。不同於虛擬機器模擬完整硬體環境,容器分享宿主機的作業系統核心,透過名稱空間和控制群組等機制,限制應用程式可存取的資源範圍。這使得容器的啟動速度更快,資源消耗更低,但也帶來了不同的安全挑戰。
graph LR A[虛擬機器] --> B(Hypervisor) B --> C[作業系統 1] B --> D[作業系統 2] E[容器] --> F(作業系統核心) F --> G[容器 1] F --> H[容器 2]
圖表翻譯
此圖示展現虛擬機器與容器在架構上的根本差異。虛擬機器透過 Hypervisor 管理,每個虛擬機器都擁有獨立的作業系統。而容器則分享宿主機的作業系統核心,透過名稱空間和控制群組等機制達到隔離效果。
{
"resources": {
"memory": {
"limit": 104857600
},
"cpu": {
"shares": 256
}
},
"namespaces": [
{"type": "pid"},
{"type": "network"},
{"type": "ipc"},
{"type": "uts"},
{"type": "mount"}
],
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"SETGID",
"SETUID"
]
}
內容解密
這段 JSON 設定檔定義了容器的資源限制和名稱空間組態。resources
區塊限制了容器可用的記憶體和 CPU 資源。namespaces
區塊定義了容器使用的名稱空間型別,包括程式 ID、網路、IPC、UTS 和掛載名稱空間,確保容器之間的隔離性。capabilities
區塊則列出了容器具有的許可權,用於控制容器在宿主機上的操作能力。
graph LR A[Docker Client] --> B(Docker Daemon) B --> C{構建映像} C --成功--> D[映像儲存函式庫] C --失敗--> E[錯誤處理]
圖表翻譯
此圖示說明 Docker 映像構建流程。Docker Client 傳送構建請求給 Docker Daemon,Docker Daemon 負責實際構建映像。構建成功後,映像會被推播到映像儲存函式庫;若構建失敗,則會進行錯誤處理。
在建構容器映像時,安全性考量至關重要。docker build
命令雖然方便,但存在安全風險,尤其是在分享的構建環境中。由於 Docker Daemon 需以 root 許可權執行,惡意程式碼可能透過 docker build
命令取得宿主機的 root 許可權。因此,建議使用無守護程式的構建工具,例如 BuildKit、Podman 或 Kaniko,以提升安全性。這些工具允許在非特權模式下構建映像,有效降低安全風險。
此外,容器映像的分層結構也可能隱藏安全風險。即使敏感資訊在後續的映像層中被刪除,這些資訊仍然可能存在於先前的映像層中。因此,在構建映像時,應避免將敏感資訊寫入檔案系統,即使是暫時性的操作也應謹慎。
選擇適當的工具和策略,才能確保容器化應用程式的安全性。透過瞭解容器技術的特性和潛在風險,並採取必要的安全措施,才能有效提升系統的安全性,並充分發揮容器技術的優勢。
使用者空間的限制
使用者空間也有以下限制:
- 相容性問題:使用者空間可能與某些應用程式不相容,因為有些應用程式需要使用宿主機的使用者身份。
- 效能問題:使用者空間可能會導致效能問題,因為程式需要在不同的使用者空間中切換。
- 管理複雜性:使用者空間需要額外的管理複雜性,因為需要管理每個程式的使用者身份。
Linux 中的 IPC 和名稱空間
在 Linux 中,程式間通訊(IPC)是指不同程式之間交換資料的方法。Linux 提供了多種 IPC 機制,包括訊息佇列、分享記憶體和訊號量等。在本文中,我們將探討 Linux 中的 IPC 和名稱空間。
IPC 機制
Linux 提供了多種 IPC 機制,包括:
- 訊息佇列(Message Queues):允許程式之間傳送和接收訊息。
- 分享記憶體(Shared Memory):允許程式之間分享記憶體區域。
- 訊號量(Semaphores):允許程式之間同步存取分享資源。
名稱空間
Linux 中的名稱空間(Namespace)是指一個程式的檢視,決定了它可以看到和存取哪些系統資源。Linux 提供了多種名稱空間,包括:
- Mount 名稱空間(Mount Namespace):控制程式可以看到哪些檔案系統。
- PID 名稱空間(PID Namespace):控制程式可以看到哪些程式 ID。
- 網路名稱空間(Network Namespace):控制程式可以看到哪些網路介面和網路組態。
- 使用者名稱空間(User Namespace):控制程式可以看到哪些使用者和組。
- IPC 名稱空間(IPC Namespace):控制程式可以看到哪些 IPC 物件。
- 控制組名稱空間(Cgroup Namespace):控制程式可以看到哪些控制組組態。
示例
下面是一個示例,展示瞭如何使用 ipcmk
命令建立一個分享記憶體段,並使用 ipcs
命令檢視 IPC 物件:
$ ipcmk -M 1000
Shared memory id: 98307
$ ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 0 root 644 80 2
0x00000000 32769 root 644 16384 2
0x00000000 65538 root 644 280 2
0xad291bee 98307 ubuntu 644 1000 0
------ Semaphore Arrays --------
key semid owner perms nsems
0x000000a7 0 root 600 1
在這個示例中,我們建立了一個分享記憶體段,其 ID 為 98307。然後,我們使用 ipcs
命令檢視所有 IPC 物件,包括訊息佇列、分享記憶體段和訊號量陣列。
名稱空間隔離
名稱空間隔離是指一個程式的名稱空間與其他程式的名稱空間隔離。這樣,可以防止一個程式存取另一個程式的系統資源。
下面是一個示例,展示瞭如何使用 unshare
命令建立一個新的 IPC 名稱空間:
$ sudo unshare --ipc sh
$ ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
在這個示例中,我們建立了一個新的 IPC 名稱空間,然後使用 ipcs
命令檢視 IPC 物件。由於我們處於一個新的名稱空間中,因此我們只能看到當前名稱空間中的 IPC 物件。
Linux 中的 IPC 和名稱空間是兩個重要的概念。IPC 機制允許程式之間交換資料,而名稱空間則決定了一個程式可以看到和存取哪些系統資源。透過使用名稱空間隔離,可以防止一個程式存取另一個程式的系統資源。瞭解這些概念對於編寫高效和安全的 Linux 程式至關重要。
圖表翻譯
graph LR A[IPC] --> B[訊息佇列] A --> C[分享記憶體] A --> D[訊號量] B --> E[程式間通訊] C --> F[分享記憶體區域] D --> G[同步存取分享資源] H[名稱空間] --> I[Mount 名稱空間] H --> J[PID 名稱空間] H --> K[網路名稱空間] H --> L[使用者名稱空間] H --> M[IPC 名稱空間] H --> N[控制組名稱空間] I --> O[檔案系統] J --> P[程式 ID] K --> Q[網路介面] L --> R[使用者和組] M --> S[IPC 物件] N --> T[控制組組態]
圖表解釋
該圖表展示了 Linux 中的 IPC 和名稱空間概念。IPC 機制包括訊息佇列、分享記憶體和訊號量等。名稱空間則決定了一個程式可以看到和存取哪些系統資源,包括 Mount 名稱空間、PID 名稱空間、網路名稱空間、使用者名稱空間、IPC 名稱空間和控制組名稱空間等。每個名稱空間都對應著特定的系統資源,例如檔案系統、程式 ID、網路介面、使用者和組、IPC 物件和控制組組態等。
容器化過程的隔離性探討
在前面的章節中,我們討論了名稱空間和控制群組的概念,並瞭解了它們如何共同作用以實作容器化過程的隔離。現在,讓我們從宿主機的角度來觀察容器化過程,以便更深入地理解其工作原理。
容器化過程從宿主機的角度觀察
雖然我們常常將其稱為「容器」,但更準確地說,它們應該被稱為「容器化過程」。因為從本質上講,容器化過程仍然是一個Linux過程,執行在宿主機上,只是它們看到的環境是被限制的,並且只能存取檔案系統和資源的子集。作為一個普通的Linux過程,容器化過程存在於宿主作業系統的上下文中,並與宿主共用同一個核心,如圖4.3所示。
圖4.3:容器化過程與宿主共用同一個核心
在下一章中,我們將比較容器化技術與虛擬機器技術。但在此之前,讓我們更深入地探討容器化過程與宿主之間的隔離程度,以及它們之間的相互作用。
實驗:觀察Docker容器化過程
為了更好地理解這些概念,讓我們進行一些實驗。首先,啟動一個根據Ubuntu的Docker容器,並在其中執行一個命令列shell。然後,在shell中執行一個長時間執行的命令,例如sleep 1000
。
$ docker run --rm -it ubuntu bash
root@1551d24a:/$ sleep 1000
在這個實驗中,sleep 1000
命令作為一個獨立的過程執行在容器內。當你按下Enter鍵執行這個命令時,Linux會克隆一個新的過程,並在其中執行sleep
可執行檔案。
你可以將這個過程轉移到背景模式(使用Ctrl-Z暫停過程,然後使用bg %1
將其轉移到背景)。然後,在容器內使用ps
命令觀察這個過程,以瞭解它從容器的角度看起來如何。
me@myhost:~$ docker run --rm -it ubuntu bash
root@ab6ea36fce8e:/$ sleep 1000
^Z
[1]+ Stopped sleep 1000
root@ab6ea36fce8e:/$ bg %1
[1]+ sleep 1000 &
這些實驗有助於我們更好地理解容器化過程與宿主之間的隔離程度,以及它們如何共用資源和核心。
虛擬機器和容器:安全性和隔離
在電腦系統中,虛擬機器(Virtual Machine,VM)和容器(Container)是兩種不同的技術,用於實作多個作業系統或應用程式在同一物理機器上執行。虛擬機器透過軟體模擬了一個完整的電腦環境,包括作業系統、應用程式和硬體資源,而容器則是在同一作業系統上執行多個隔離的應用程式。
虛擬機器的安全性主要依靠其作業系統和虛擬機器監視器(Hypervisor)的安全性。虛擬機器監視器是一種軟體,負責管理和分配物理機器的資源給虛擬機器。虛擬機器之間的隔離是透過虛擬機器監視器實作的,每個虛擬機器都有自己的作業系統和應用程式,彼此之間無法直接存取。
容器則是在同一作業系統上執行多個隔離的應用程式。容器透過作業系統的名稱空間(Namespace)和控制組(Cgroup)等機制實作隔離,每個容器都有自己的檔案系統、網路和程式空間,彼此之間無法直接存取。
虛擬機器和容器的比較
虛擬機器和容器都可以實作多個作業系統或應用程式在同一物理機器上執行,但是它們之間有許多不同:
- 虛擬機器需要一個完整的作業系統,而容器則是在同一作業系統上執行多個隔離的應用程式。
- 虛擬機器之間的隔離是透過虛擬機器監視器實作的,而容器則透過作業系統的名稱空間和控制組等機制實作隔離。
- 虛擬機器的啟動速度較慢,而容器的啟動速度較快。
- 虛擬機器需要更多的資源,而容器則需要較少的資源。
虛擬機器和容器的安全性
虛擬機器和容器都有自己的安全性問題。虛擬機器的安全性主要依靠其作業系統和虛擬機器監視器的安全性,而容器的安全性則主要依靠作業系統的名稱空間和控制組等機制的安全性。
虛擬機器之間的隔離是透過虛擬機器監視器實作的,如果虛擬機器監視器存在安全漏洞,則可能導致虛擬機器之間的隔離失敗。同樣,容器之間的隔離是透過作業系統的名稱空間和控制組等機制實作的,如果這些機制存在安全漏洞,則可能導致容器之間的隔離失敗。
結論
虛擬機器和容器都是實作多個作業系統或應用程式在同一物理機器上執行的技術,但是它們之間有許多不同。虛擬機器需要一個完整的作業系統,而容器則是在同一作業系統上執行多個隔離的應用程式。虛擬機器之間的隔離是透過虛擬機器監視器實作的,而容器則透過作業系統的名稱空間和控制組等機制實作隔離。虛擬機器和容器都有自己的安全性問題,需要注意其安全性問題以避免安全漏洞。
第6章:容器映像
映像組態
在第3章和第4章中,您已經瞭解瞭如何建立容器,現在讓我們來看看一個config.json
檔案的片段。這個檔案包含了很多您已經熟悉的內容。下面是一個示例片段:
"linux": {
"resources": {
"memory": {
"limit": 1000000
},
"devices": [
{
"allow": false,
"access": "rwm"
}
]
},
"namespaces": [
{
"type": "pid"
},
{
"type": "network"
},
{
"type": "ipc"
},
{
"type": "uts"
},
{
"type": "mount"
}
]
}
如您所見,組態包括了建立容器所需的所有資訊,包括受控制組限制的資源列表和需要建立的名稱空間。
映像構建101
大多數人使用docker build
命令來構建容器映像,該命令根據Dockerfile
中的指令建立映像。在討論構建之前,我想提醒您要謹慎地對待docker build
命令,以避免安全風險。
Docker 正在開發一個特殊模式,該模式不需要超級使用者許可權,並旨在解決以下部分中描述的問題。然而,在本章寫作時,這個模式仍處於測試階段。
構建命令的危險
當您執行docker build
命令時,Docker 命令列工具本身並不執行太多操作。它只是將命令轉換為 Docker API 請求,並透過 Docker 通訊端傳送給 Docker 守護程式。任何具有存取 Docker 通訊端許可權的程式都可以向 Docker 守護程式傳送 API 請求。
Docker 守護程式是一個長期執行的程式,實際上負責啟動和管理容器及其映像。如您在第4章中所見,為了建立容器,守護程式需要建立名稱空間的能力,因此它必須以超級使用者身份執行。
假設您想為構建容器映像和將其儲存到登入檔而分配一臺機器(或虛擬機器)。在這種情況下,您的機器上必須執行 Docker 守護程式,其功能遠遠超過了構建映像和與登入檔互動。沒有額外的安全措施,任何可以在該機器上執行docker build
命令的使用者也可以執行docker run
命令來在該機器上執行任意命令。
無守護程式構建
為了避免這些安全風險,建議使用不依賴於 Docker 守護程式的替代容器映像構建工具。一個這樣的工具是 BuildKit,它是 Docker 實驗性無超級使用者許可權構建模式的基礎。
其他非特權構建工具包括 Podman(https://podman.io/),它提供了類別似於`docker build`的功能,但不需要 Docker。另一個工具是 Kaniko(https://github.com/GoogleContainerTools/kaniko),它可以在 Kubernetes 叢集中構建容器映像,而無需存取 Docker 守護程式。
映像層
無論使用哪種工具,大多數容器映像構建都在 Dockerfile
中描述。Dockerfile
允許您指定一系列指令,每個指令都會生成一個檔案系統層或更改映像組態。
檔案系統層中的敏感資料
任何擁有容器映像的人都可以存取其中的所有檔案。從安全形度來看,在映像中包含敏感資料(如密碼或令牌)不是一個好主意。(在第12章中,我將討論如何處理此類別資料。)
所有層都儲存單獨,因此您需要小心,不要意外地儲存敏感資料,即使後續層刪除了它們。以下是一個不良的 Dockerfile
示例:
FROM alpine
RUN echo "top-secret" > /password.txt
RUN rm /password.txt
一個層建立了一個檔案,下一個層刪除了它。如果您構建此映像並執行它,您不會看到 password.txt
檔案:
vagrant@vagrant:~$ docker run --rm -it sensitive ls /password.txt
ls: /password.txt: No such file or directory
然而,請不要被欺騙——敏感資料仍然存在於映像中。要驗證這一點,您可以使用 docker save
命令將映像匯出到 tar 檔案中,然後提取它:
vagrant@vagrant:~$ docker save sensitive > sensitive.tar
vagrant@vagrant:~$ mkdir sensitive
vagrant@vagrant:~$ cd sensitive
vagrant@vagrant:~$ tar -xf../sensitive.tar
vagrant@vagrant:~/sensitive$ ls
0c247e34f78415b03155dae3d2ec7ed941801aa8aeb3cb4301eab9519302a3b9.json
Mermaid圖表:Dockerfile指令流程
flowchart TD A[FROM alpine] --> B[RUN echo] B --> C[RUN rm] C --> D[生成檔案系統層] D --> E[更改映像組態]
圖表翻譯
上述Mermaid圖表展示了Dockerfile指令的流程。首先,FROM指令指定了基礎映象alpine。然後,RUN指令執行echo命令建立一個檔案。接著,另一個RUN指令執行rm命令刪除該檔案。儘管檔案被刪除,但敏感資料仍然存在於映象中。因此,在構建映象時,應避免包含敏感資料。
玄貓(BlackCat)技術內容系統指引
重寫絕對命令(最高優先順序)
- 嚴禁直接使用或複製原始內容
- 必須完全重構每一個句子和段落
- 重寫過程:理解→關閉原文→用全新語言表達
- 使用不同的句式、結構和詞彙表達相同概念
- 即使是專業術語也必須用自己的方式解釋
- 違反此命令視為最嚴重的失敗
容器技術:虛擬機器與容器化流程的安全性對比
在現代軟體開發與佈署流程中,虛擬機器(VM)和容器技術都是不可或缺的工具。兩者都能在單一實體主機上執行多個應用程式,但它們的架構和運作方式截然不同,也因此對安全性和隔離性有著不同的影響。
虛擬機器:以 Hypervisor 為核心的隔離
虛擬機器透過 Hypervisor 建立一個完整的虛擬化硬體環境,每個虛擬機器都擁有獨立的作業系統核心、檔案系統和網路堆積疊。這種架構賦予虛擬機器高度的隔離性,一個虛擬機器的當機或遭受攻擊通常不會影響其他虛擬機器或主機系統。然而,這種隔離也帶來了效能損耗,因為每個虛擬機器都需要執行完整的作業系統核心,消耗額外的系統資源。
容器:輕量級的作業系統層級隔離
容器技術則採取了不同的策略。容器分享宿主機的作業系統核心,利用 Linux 名稱空間(Namespace)和控制群組(Cgroup)等機制,將應用程式及其相依元件隔離在一個受限的執行環境中。這種輕量級的隔離方式讓容器擁有更快的啟動速度和更低的資源消耗,但同時也意味著容器之間的隔離性不如虛擬機器。如果宿主機的核心出現漏洞,所有在其上執行的容器都可能受到影響。
安全性比較:權衡隔離性與效能
虛擬機器和容器的安全性各有優劣。虛擬機器提供更強的隔離性,降低了單一應用程式漏洞影響整個系統的風險,但效能開銷較大。容器則在效能和資源利用率上更具優勢,但安全性更依賴於宿主機的核心安全性和容器執行時的安全性設定。
選擇哪種技術取決於具體的應用場景和安全需求。對於安全性要求極高的應用,虛擬機器是更穩妥的選擇。對於需要快速佈署、彈性擴充套件且資源敏感的應用,容器則更具優勢。
graph LR subgraph 虛擬機器 A[Hypervisor] --> B(VM 1); A --> C(VM 2); B --> D[Guest OS 1]; C --> E[Guest OS 2]; end subgraph 容器 F[Host OS Kernel] --> G(Container 1); F --> H(Container 2); end
圖表翻譯
此圖示說明瞭虛擬機器和容器在架構上的差異。虛擬機器透過 Hypervisor 管理多個虛擬機器,每個虛擬機器都執行獨立的 Guest OS。而容器則分享 Host OS Kernel,透過名稱空間和控制群組實作隔離。
深入探討容器映像:從建構到安全性
容器映像的建構過程和安全性是容器技術中至關重要的環節。瞭解映像的組成和建構方式,才能有效管理容器的安全風險,並建構更安全可靠的容器化應用程式。
映像組態:config.json
揭秘
容器映像的組態資訊儲存在 config.json
檔案中,其中包含了容器運作所需的各種設定,例如資源限制、名稱空間設定等等。透過檢視 config.json
,開發者可以瞭解容器的執行環境和資源組態,進而進行調校和最佳化。
映像建構:docker build
的潛在風險
docker build
命令是建構容器映像的常用工具,它根據 Dockerfile
中的指令逐步建立映像。然而,使用 docker build
需要謹慎,因為它需要 Docker 守護程式以 root 許可權執行,這可能帶來安全風險。
如果攻擊者取得了 Docker 通訊端的存取權,就能夠控制 Docker 守護程式,進而執行任意程式碼。因此,在建構容器映像時,應避免在映像中包含敏感資訊,例如密碼或金鑰。
安全的映像建構方案:非 root 許可權建構
為了降低安全風險,建議使用非 root 許可權的映像建構工具,例如 BuildKit、Podman 和 Kaniko。這些工具可以在不需要 root 許可權的情況下建構容器映像,有效提升安全性。
映像層級與資料安全:避免敏感資訊洩漏
容器映像是由多個層級組成的,每個層級都包含了檔案系統的變更。即使在後續層級中刪除檔案,先前層級中的檔案仍然存在於映像中。這意味著,如果在建構過程中不慎將敏感資訊寫入檔案,即使之後刪除該檔案,敏感資訊仍然可能被還原。
graph LR A[基礎映像] --> B[層級 1] B --> C[層級 2 (刪除檔案)] C --> D[最終映像 (檔案仍存在)]
圖表翻譯
此圖示說明瞭容器映像的層級結構。即使在層級 2 中刪除檔案,該檔案仍然存在於最終映像中,因為每個層級都是獨立儲存的。
因此,在建構容器映像時,應避免將敏感資訊寫入檔案系統。如果必須使用敏感資訊,應採用安全的儲存方式,例如使用 secrets management 工具。
容器技術的安全性是一個複雜的議題,需要開發者深入理解容器的架構和運作方式,才能有效管理安全風險。從虛擬機器和容器的比較,到容器映像的建構和安全性,本文提供了一個全面的概述,希望能幫助讀者更好地理解容器技術的安全性,並在實務中建構更安全的容器化應用程式。選擇適合的技術方案,並遵循最佳實務,才能確保容器化應用程式的安全性和穩定性。