返回文章列表

SELinux容器互動與網路隔離管理

本文探討 SELinux 與容器的互動機制,包含 SELinux 策略管理、容器相關策略設定、以及如何使用 Udica 工具生成自定義 SELinux 安全組態檔。同時也解析容器網路隔離的原理與 Podman 的網路管理功能,涵蓋橋接網路、主機網路等組態實踐,並比較無根容器與有根容器的差異,最後說明 CNI

容器技術 系統安全

SELinux 根據型別強制機制,為容器提供更細緻的存取控制,不僅隔離容器間的資源,也保護主機系統免受容器內惡意程式碼的威脅。容器的檔案系統在執行時會被 SELinux 標記,例如 MergedDir 中的可寫入檔案會被標記為 container_file_t 並帶有 MCS 類別,而唯讀層則標記為 container_ro_file_t。Udica 工具可以協助生成自定義 SELinux 策略,解決容器因許可權不足無法存取主機資源的問題,提升容器佈署的靈活性。容器網路隔離則仰賴網路名稱空間,賦予每個容器獨立的網路堆積疊,包含網路介面、路由表和防火牆規則。Podman 提供了橋接網路、主機網路等多種網路管理方式,方便使用者根據需求組態容器網路。無根容器與有根容器在與主機資源互動上有所不同,無根容器以非特權使用者執行,安全性更高,但網路設定可能更為複雜。最後,文章也介紹了 CNI 網路設定檔的結構和常用外掛程式,例如 bridge、ipvlan、host-local 等,讓開發者能更深入理解容器網路的運作機制。

SELinux 與容器的互動機制

SELinux(Security-Enhanced Linux)是一種強制性存取控制(MAC)系統,用於增強 Linux 系統的安全性。在容器化的環境中,SELinux 扮演著至關重要的角色,確保容器之間的隔離以及容器與主機之間的隔離。

SELinux 策略基礎

SELinux 的策略根據特定的模式:POLICY DOMAIN TYPE:CLASS OPERATION;。其中,POLICY 代表策略型別(如 allow、auditallow 等),DOMAIN 是行程的域,TYPE 是資源的型別上下文,CLASS 是物件的類別(如檔案、目錄等),而 OPERATION 則是一系列由策略處理的動作。

例如,一個基本的 allow 規則如下:

allow myapp_t myapp_log_t:file { read_file_perms append_file_perms };

這條規則允許在 myapp_t 域中執行的行程存取 myapp_log_t 型別的檔案,並執行讀取和追加操作。

SELinux 策略管理

SELinux 以模組化的方式管理策略,可以動態地載入和解除安裝策略模組,而無需每次都重新編譯整個策略集。使用 semodule 工具可以載入和解除安裝策略,例如:

# semodule -i custompolicy.pp

也可以用來檢視所有已載入的策略:

# semodule -l

容器相關的 SELinux 策略

在 Fedora、CentOS、RHEL 等發行版中,容器相關的策略定義在 container-selinux 套件中。該套件包含已編譯的 SELinux 模組,其原始碼可在 GitHub 上找到。

容器內的行程被標記為 container_t 域,具有對 container_file_t 型別資源的讀寫許可權,以及對 container_share_t 型別資源的讀取和執行許可權。

程式碼解析:

以下是一個範例 Plantuml 圖表,用於描述容器與 SELinux 的互動關係:

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 程式碼解析:

rectangle "labelled as" as node1
rectangle "has access to" as node2
rectangle "read/write" as node3
rectangle "read/execute" as node4

node1 --> node2
node2 --> node3
node3 --> node4

@enduml

此圖示展示了容器內的行程如何與不同型別的資源互動。

內容解密:

  1. graph LR; 定義了一個從左到右的圖表方向。
  2. A[Container] -->|labelled as|> B(container_t); 表示容器被標記為 container_t 域。
  3. B -->|has access to|> C(container_file_t);B -->|has access to|> D(container_share_t); 描述了 container_t 域對不同資源型別的存取許可權。
  4. C -->|read/write|> E[Resources];D -->|read/execute|> F[Resources]; 詳細說明瞭對這些資源的操作許可權。

容器與 SELinux 的互動例項

當建立一個容器時,其映像的唯讀層被標記為 container_ro_file_t 型別,以防止容器寫入這些目錄。同時,MergedDir 被標記為 container_file_t 型別,是可寫入的。

實驗驗證:

執行一個具有特定 MCS(Multi-Category Security)類別的容器:

# podman run -d --name selinux_test1 --security-opt label=level:s0:c1,c2 nginx

然後,可以在主機檔案系統上找到標記為 container_file_t:s0:c1,c2 的檔案:

# find /var/lib/containers/storage/overlay -type f -context '*container_file_t:s0:c1,c2*' -printf '%-50Z%p\n'

程式碼解析:

# podman run -d --name selinux_test1 --security-opt label=level:s0:c1,c2 nginx

此命令啟動了一個名為 selinux_test1 的容器,並指定了 SELinux 的 MCS 類別。

# find /var/lib/containers/storage/overlay -type f -context '*container_file_t:s0:c1,c2*' -printf '%-50Z%p\n'

此命令用於在主機上查詢具有特定 SELinux 上下文的檔案。

內容解密:

  1. 使用 --security-opt label=level:s0:c1,c2 指定容器的 SELinux 標籤。
  2. find 命令用於搜尋具有特定 SELinux 上下文的檔案。
  3. -printf '%-50Z%p\n' 用於格式化輸出,顯示檔案的安全上下文和路徑。

容器安全與 SELinux 互動探討

在容器化的環境中,安全性是一個至關重要的議題。SELinux(Security-Enhanced Linux)作為一個強制性存取控制(MAC)系統,為 Linux 提供了額外的安全層。本文將探討 SELinux 如何與容器互動,以及如何利用工具如 Udica 來生成自定義的 SELinux 安全組態檔,以解決容器存取主機資源的許可權問題。

SELinux 對容器的標籤處理

當容器執行時,SELinux 會為容器的檔案系統賦予特定的標籤。在前面的範例中,我們觀察到容器的 MergedDir 目錄下的檔案都被標記為 container_file_t,並且附帶了特定的 MCS(Multi-Category Security)類別,如 s0:c1,c2。這確保了只有被賦予相同 MCS 類別的容器才能存取這些檔案。

# ls -alZ /var/lib/containers/storage/overlay/4b147975bb5c336b10e71d21c49fe88ddb00d0569b77ddab1d7737f80056677b/merged/lib/terminfo/r/rxvt-unicode-256color
system_u:object_r:container_file_t:s0:c1,c2 /var/lib/containers/storage/overlay/4b147975bb5c336b10e71d21c49fe88ddb00d0569b77ddab1d7737f80056677b/merged/lib/terminfo/r/rxvt-unicode-256color

另一方面,容器的 LowerDir 層被標記為 container_ro_file_t,表示這些層是唯讀的,並且不包含 MCS 類別,因為它們可能被多個容器共用。

# ls -alZ /var/lib/containers/storage/overlay/2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f/diff
dr-xr-xr-x. 21 root root unconfined_u:object_r:container_ro_file_t:s0 4096 Jan  5 23:16 .
drwxr-xr-x. 2 root root unconfined_u:object_r:container_ro_file_t:s0 4096 Dec 20 00:00 bin

內容解密:

  • 這裡展示了 SELinux 如何對容器的不同層級進行標籤處理,以確保容器的安全性和隔離性。
  • container_file_tcontainer_ro_file_t 是 SELinux 用於區分容器內可讀寫檔案和唯讀檔案的標籤。
  • MCS 類別的使用進一步加強了容器的隔離,確保不同容器間的檔案無法互相存取。

Udica:生成自定義 SELinux 安全組態檔

Udica 是一個開源工具,旨在解決容器因 SELinux 限制而無法存取特定主機資源的問題。透過分析容器的組態,Udica 能夠生成自定義的 SELinux 安全組態檔,讓容器能夠安全地存取所需的資源。

安裝 Udica

在 Fedora 系統上,可以透過以下命令安裝 Udica:

$ sudo dnf install -y udica setools-console container-selinux

使用 Udica 生成自定義組態檔

首先,建立一個需要寫入主機 /var/log 目錄的容器。由於預設的 container_t 域不允許寫入標記為 var_log_t 的目錄,因此需要使用 Udica 生成自定義的安全組態檔。

  1. 建立容器並測試預設行為

    建立一個名為 custom_logger 的容器,該容器執行一個指令碼,不斷向 /var/log/custom.log 寫入日誌。

    FROM docker.io/library/fedora
    COPY logger.sh /
    CMD ["/logger.sh"]
    

    建構並執行容器後,由於 SELinux 的限制,容器無法寫入 /var/log/custom.log

    # podman run -v /var/log:/var/log:rw --name custom_logger1 custom_logger
    tee: /var/log/custom.log: Permission denied
    
  2. 使用 Udica 生成自定義組態檔

    # podman inspect custom_logger1 > container.json
    # udica -j container.json custom_logger
    

內容解密:

  • Udica 透過分析 container.json 檔案,瞭解容器的執行需求,並生成相應的 SELinux 組態檔。
  • 這種方法允許容器在不犧牲安全性的前提下,存取特定的主機資源。
  • 自定義組態檔的生成,大大簡化了容器與 SELinux 之間的相容性問題,使得容器的佈署更加靈活和安全。

容器網路隔離與管理

容器網路隔離利用網路名稱空間為每個容器提供獨立的網路堆積疊。如果沒有容器執行環境,跨多個名稱空間管理網路介面將會非常複雜。Podman 提供靈活的網路管理功能,讓使用者可以自定義容器如何與外部容器以及同一主機內的其他容器進行通訊。

瞭解容器網路組態實踐

在本章中,我們將探討管理容器網路的常見組態實踐,並瞭解無根(rootless)容器與有根(rootfull)容器之間的差異。Podman 的網路管理功能允許使用者根據需求自定義容器的網路組態。

網路名稱空間與容器隔離

每個容器都有自己的網路名稱空間,這意味著每個容器都有獨立的網路堆積疊,包括自己的網路介面、路由表和防火牆規則。這種隔離確保了容器之間的網路流量是分離的,從而提高了安全性和靈活性。

Podman 網路管理功能

Podman 提供了多種網路管理功能,讓使用者可以根據需求組態容器的網路。以下是一些主要的網路組態選項:

  1. 橋接網路:Podman 可以使用橋接網路讓容器之間進行通訊。橋接網路允許容器分享相同的網路名稱空間,但仍然保持自己的獨立網路組態。
  2. 主機網路:容器可以直接使用主機的網路堆積疊,這樣可以提高網路效能,但會降低隔離性。
  3. 無網路:容器可以組態為不使用任何網路,這對於某些不需要網路存取的應用程式非常有用。

網路組態範例

以下是一個使用 Podman 組態橋接網路的範例:

# 建立一個新的橋接網路
podman network create my_bridge

# 執行一個容器並將其連線到新的橋接網路
podman run -d --name my_container --net my_bridge my_image

網路組態解密

  1. podman network create my_bridge:建立一個新的橋接網路,名稱為 my_bridge
  2. podman run -d --name my_container --net my_bridge my_image:執行一個新的容器,名稱為 my_container,並將其連線到 my_bridge 網路。

無根容器與有根容器的差異

無根容器與有根容器的主要差異在於它們如何與主機的資源互動。無根容器以非特權使用者的身份執行,而有根容器則以 root 使用者的身份執行。這種差異影響了容器的網路組態和管理。

本章重點回顧

  • 瞭解容器網路隔離的基本原理
  • 學習 Podman 的網路管理功能
  • 瞭解無根容器與有根容器的差異
  • 實際操作範例:使用 Podman 組態橋接網路

進一步閱讀

  • Podman 網路管理檔案:https://docs.podman.io/en/latest/markdown/podman-network.1.html
  • 容器網路隔離原理:https://www.kernel.org/doc/html/latest/networking/namespace.html

容器網路與 Podman 設定

本章節將涵蓋以下主要主題:

  • 容器網路與 Podman 的設定
  • 連線兩個或多個容器
  • 將容器暴露在主機外部
  • 無根容器網路行為

技術需求

要完成本章節,您需要一台具有正常運作的 Podman 安裝的機器。如同在第三章《執行第一個容器》中提到的,本文中的所有範例都可以在 Fedora 34 系統或更新版本上執行,但也可以在您選擇的作業系統上重現。本章節中的範例將與 Podman v3.4.z 和 Podman v4.0.0 相關,因為它們提供了不同的網路實作。

要理解我們將要涵蓋的容器網路主題,將有助於您對第四章《管理執行中的容器》、第五章《為容器的資料實作儲存》以及第九章《將映像推播到容器登入檔》中所涵蓋的主題有良好的理解。同時,您也需要對基本網路概念(如路由、IP 協定、DNS 和防火牆)有良好的理解。

容器網路與 Podman 設定

在本文中,我們將介紹 Podman 的網路實作以及如何設定網路。Podman 4.0.0 對網路堆積疊進行了重要的更改。然而,Podman 3 在社群中仍被廣泛使用。因此,我們將涵蓋這兩種實作。

CNI 設定快速入門

典型的 CNI 設定檔定義了外掛程式列表及其相關設定。以下範例顯示了在 Fedora 上進行全新 Podman 安裝的預設 CNI 設定:

{
  "cniVersion": "0.4.0",
  "name": "podman",
  "plugins": [
    {
      "type": "bridge",
      "bridge": "cni-podman0",
      "isGateway": true,
      "ipMasq": true,
      "hairpinMode": true,
      "ipam": {
        "type": "host-local",
        "routes": [{ "dst": "0.0.0.0/0" }],
        "ranges": [
          [
            {
              "subnet": "10.88.0.0/16",
              "gateway": "10.88.0.1"
            }
          ]
        ]
      }
    },
    {
      "type": "portmap",
      "capabilities": {
        "portMappings": true
      }
    },
    {
      "type": "firewall"
    },
    {
      "type": "tuning"
    }
  ]
}

內容解密:

此 JSON 設定檔定義了 Podman 使用的 CNI 網路設定。主要欄位說明如下:

  1. "cniVersion": 指定 CNI 版本。

  2. "name": 網路名稱,這裡是 "podman"

  3. "plugins": 一個陣列,包含用於設定網路的外掛程式列表。

    • 第一個外掛程式是 "bridge" 型別,用於建立一個 Linux 網橋(cni-podman0),並將容器介面連線到此網橋上。

      • "isGateway": 表示此網橋作為閘道器。
      • "ipMasq": 設定 IP 偽裝。
      • "hairpinMode": 允許網橋上的介面與自身通訊。
      • "ipam": IP 位址管理設定,使用 "host-local" 型別來分配 IP 位址,並定義了一個子網路 (10.88.0.0/16)。
    • "portmap" 外掛程式用於設定埠對映,允許將主機的埠對映到容器的埠。

    • "firewall" 外掛程式用於管理防火牆規則。

    • "tuning" 外掛程式用於對網路介面進行調優。

這個設定展示瞭如何使用 CNI 外掛程式來管理和設定容器的網路連線。

常用的 CNI 外掛程式

CNI 社群維護了一系列參考外掛程式,這些外掛程式可以被容器執行環境使用。CNI 參考外掛程式分為介面建立、IP 位址管理(IPAM)和 Meta 外掛程式。介面建立外掛程式可以使用 IPAM 和 Meta 外掛程式。

常用的介面建立外掛程式:

  • bridge: 建立一個專用的 Linux 網橋,用於讓容器之間的通訊以及與外部系統的通訊。
  • ipvlan: 為容器附加一個 IPVLAN 介面,提供了一種與傳統 Linux 網橋不同的網路解決方案。
  • macvlan: 為容器組態 MACVLAN,每個容器的子介面都有一個 MAC 位址。
  • host-device: 將現有的介面直接傳遞給容器,目前不被 Podman 支援。

CNI IPAM 外掛程式:

  • dhcp: 在主機上執行一個守護程式,為容器管理 DHCP 租約,需要主機網路上已經有一個執行的 DHCP 伺服器。
  • host-local: 使用定義的位址範圍為容器分配 IP 位址,並將分配資料儲存在主機檔案系統中,是 Podman 網路網橋的預設 IPAM 外掛程式。
  • static: 管理一個靜態位址列表,將這些位址分配給容器。

內容解密:

這些外掛程式提供了豐富的功能,用於建立和管理容器的網路介面和 IP 位址。選擇合適的外掛程式可以根據具體需求來最佳化容器的網路組態。透過這些外掛程式,管理員可以靈活地控制容器的網路行為。