Linux 命名空間

最後更新: 2021-11-21 04:00:04 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

命名空間會將原本全域的名稱與資源檢視加以分區。 每個工作都有一個每種類型的受支援命名空間執行個體,而位於同一執行個體中的工作會觀察到 相同的執行個體專屬資源。命名空間本身不會限制 CPU、記憶體或 I/O 的使用量;這些資源控制 由 cgroup 提供。

Linux 公開八種命名空間類型。Time 命名空間在 Linux 5.6 中加入;其他類型早於該版本就已 存在。命名空間旗標的版本特定真實來源是 include/uapi/linux/sched.h。

TypeCreation flagIsolated resource
CgroupCLONE_NEWCGROUPCgroup 根檢視
IPCCLONE_NEWIPCSystem V IPC 物件與 POSIX 訊息佇列
NetworkCLONE_NEWNET網路裝置、通訊協定堆疊、路由表、防火牆狀態與連接埠空間
MountCLONE_NEWNS掛載表與掛載傳播關係
PIDCLONE_NEWPID程序 ID 編號空間與 PID 1 語意
TimeCLONE_NEWTIMEmonotonic clock 與 boot-time clock 的偏移
UserCLONE_NEWUSER使用者與群組 ID 對映、認證資料及具範圍的 capability
UTSCLONE_NEWUTS主機名稱與 NIS 網域名稱

初始系統會從每種類型各有一個執行個體開始。命名空間識別碼不是可攜且具有全域意義的 32 位元 ID。相反地,/proc/PID/ns 會以符號連結公開命名空間控制代碼,例如 net:[4026531840];其中的數字識別該核心物件在命名空間檔案系統中的 inode。

sh
ls -l /proc/self/ns
lsns
readlink /proc/self/ns/net

建立與加入命名空間 #

傳入一個或多個 CLONE_NEW* 旗標時,clone(2) 與 clone3(2) 會在新的命名空間中建立 子工作。unshare(2) 會讓呼叫工作使用新建立的命名空間,但須遵循各命名空間類型的規則。 PID 命名空間與 Time 命名空間的 unshare 只會影響之後建立的子工作;呼叫者仍留在目前的 PID 或 Time 命名空間中。setns(2) 會加入由已開啟檔案描述元參照的命名空間。 unshare(1) 與 nsenter(1) 是適合實驗與管理的便利前端工具。

sh
# New user, mount, and PID namespaces; the child becomes PID 1 in its PID namespace.
unshare --user --map-root-user --mount --pid --fork --mount-proc sh

# Join the mount namespace of an existing process.
sudo nsenter --target "$PID" --mount sh

命名空間操作不可互相替代:

  • 新的 PID 命名空間會影響之後建立的子工作。工作無法將自身移動到另一個 PID 命名空間; 對其呼叫 setns() 會使後續子工作在該命名空間中可見。
  • 新的掛載命名空間起初會複製呼叫者的掛載表。共享掛載傳播仍可能在命名空間之間傳遞 掛載,因此容器設定通常會在掛載檔案系統前,將相關樹設為 private 或 slave。
  • 網路命名空間啟動時包含必須啟用的 loopback 裝置。將 veth 端點移入命名空間是連接它的 常見方式。
  • User 命名空間的建立具有不同於其他命名空間類型的認證資料與對映規則。不要假設 setns() 成功就會授予主機權限。

核心會在相關 User 命名空間中檢查 capability,並拒絕不安全的轉換。由於確切的錯誤條件 會因命名空間類型與核心版本而異,呼叫者應使用系統呼叫手冊頁和目標核心原始碼。

控制代碼與生命週期 #

/proc/PID/ns/<type> 可以作為命名空間檔案描述元開啟。只要仍有工作或命名空間檔案描述元 參照它,命名空間就會繼續存在。對此項目進行 bind mount 也會保留參照,因此監督程式可以在 最後一個程序結束後保留命名空間:

sh
sudo mkdir -p /run/netns/demo
sudo touch /run/netns/demo
sudo mount --bind /proc/"$PID"/ns/net /run/netns/demo
ip netns exec demo ip link

不再需要時必須卸載 bind mount。應將命名空間控制代碼視為具備權限的物件:對 /proc/PID/ns 的存取受 /proc 可見性和 ptrace 型存取檢查約束,而繼承的描述元可能允許 接收者呼叫 setns()。

User 命名空間與 ID 對映 #

User 命名空間會將命名空間內的連續 ID 範圍對映到其父 User 命名空間中的 ID。建立者通常 可以透過 --map-root-user 取得命名空間本地的有效 UID 0,但該身分不會自動擁有未對映 主機 ID 或全域主機資源的權限。

在非特權設定中,監督程式通常先寫入 uid_map,接著向 setgroups 寫入 deny,最後寫入 gid_map。這個順序可防止程序透過未對映群組的轉換取得以群組為基礎的存取權。對映規則 是刻意設為嚴格限制;委派的範圍以及 newuidmap 和 newgidmap 輔助程式會處理呼叫者 無法直接寫入的對映。

Capability 是相對於 User 命名空間評估的。子 User 命名空間中具有 CAP_SYS_ADMIN 的程序 可以執行由該命名空間管理的操作,例如設定其掛載命名空間,但它在初始 User 命名空間中 不具備該 capability。祖先命名空間擁有的核心物件、裝置存取權與 LSM 原則仍然構成安全 邊界。

容器的組合 #

容器是多種隔離機制的組合,而非單一命名空間。一種實用的設定順序如下:

  1. 建立 User 命名空間並安裝明確的 UID/GID 對映,因為它會為後續設定提供命名空間範圍的 capability。
  2. 建立掛載命名空間,明確變更傳播方式,並建構檔案系統檢視。
  3. 建立 PID 命名空間,fork 預定的 init 程序,並從掛載命名空間掛載新的 /proc,使程序 報告符合 PID 檢視。
  4. 視需要建立或加入 Network、IPC、UTS、Time 與 cgroup 命名空間;從具特權的主機端管理器 設定網路連結。
  5. 在執行前將工作負載置於主機管理的 cgroup 中,並保留命名空間檔案描述元以供檢查與拆除。

PID 1 需要明確處理,因為它會回收孤兒子程序,且具有特殊的訊號行為。僅建立 PID 命名空間 的最小容器命令可能留下殭屍程序或忽略終止訊號;執行階段需要 init/reaper 或等效的監督 機制。

掛載命名空間控制檔案系統掛載的可見性,而 chroot(2) 只會變更路徑名稱解析。為避免暴露 或接收非預期的掛載,必須將兩者與審慎的掛載傳播設定結合。

Cgroup 與 Cgroup 命名空間 #

Cgroup 命名空間會將工作之 cgroup 根路徑虛擬化。它會變更工作從 /proc/PID/cgroup 以及 掛載於該命名空間中的 cgroup 檔案系統讀取的內容,從而防止內部工作負載發現祖先路徑。 它不會建立獨立的 cgroup 階層,也不會削弱主機的 CPU、記憶體、I/O 或 PID 限制。

如需完整的命名空間 ABI 說明,請將相關的 namespaces(7) 手冊頁 與目標版本的 核心原始碼 一併參閱。

評論

在 GitHub 上閱讀或評論(發表評論需要登入)