程序如何檢測到它在子命名空間中?
我研究了這個主題,並在 Github 上找到了以下程式碼,上面寫著:
// HasNamespace determines if a container is using a particular namespace or the // host namespace. // The device number of an unnamespaced /proc/1/ns/{ns} is 4 and anything else is // higher. // Only works from inside a container.
但是,該評論已過時,如下所示:
$ docker run -ti --rm --pid host debian root@e29ab2d7176b:/# stat --format="%d" /proc/self/ns/net 58 root@e29ab2d7176b:/# stat --format="%d" /proc/self/ns/pid 58
如果該評論是正確的,這裡
stat --format="%d" /proc/self/ns/pid
應該導致 4。程序如何檢測它是否在子命名空間中?
我可以分享我前段時間自己研究該主題時發現的內容。它當然不是權威的,也不是詳盡的,但它可能會有所幫助。
技術說明
免責聲明
我本人從未真正實施過我將要描述的任何方法。它們只是我為前一段時間從事的一個自定義容器化項目考慮的可能性,但後來決定完全放棄命名空間檢測。如果有興趣,請繼續閱讀。
這是一種通過利用核心觀察到的行為來檢測初始命名空間的方法。(但要注意這些行為不是官方 API)。這種方法可以在最常見和健全的設置下工作,儘管它們可能並非總是如此。
“引導”命名空間
從核心 v3.8到目前最新的穩定版v5.11(和目前 v5.12-rc),初始IPC、UTS、使用者、PID、cgroup 和時間命名空間始終具有特定的硬編碼 ID,如下所示。因此,我們可以安全地假設對於這些命名空間類型,任何大於固定命名空間 ID 的命名空間 ID 都可能被視為子命名空間:
IPC = 0xEFFFFFFF UTS = 0xEFFFFFFE USER = 0xEFFFFFFD PID = 0xEFFFFFFC CGROUP = 0xEFFFFFFB TIME = 0xEFFFFFFA
上面的列表取自 v5.12-rc6 源,但這些值自 v3.8 以來一直相同,當然除了在 v3.8 中根本不存在的命名空間(“cgroup”已添加到v4.6,而 v5.6 中的“時間”)。
請注意這些初始命名空間是如何添加的(多年來),其值“增長”向下。相反,所有子命名空間都採用順序(按需)值,從
0xF0000000
.因此,對於沒有祖先程序覆蓋初始命名空間的設置,這些固定值可以非常巧妙地解決這些命名空間的“檢測任務”。
但是,讓我重申一下,這些值根本不是暴露給使用者空間的任何官方 API 的一部分(甚至不包括核心空間 AFAICT),因此它們將來可能會發生變化。
核心開發人員甚至可能選擇讓它們全部是動態的甚至是隨機的。事實上,您可能會注意到掛載和網路命名空間不在該列表中,這是因為所有網路和掛載命名空間(包括初始命名空間)都已經完全動態,並且總是
0xF0000000
像任何子命名空間一樣採用從開頭的 ID 。因此,對於掛載和網路初始命名空間,即使在最友好的條件下,仍然必須進行一些啟發式分析。掛載命名空間
根據我目前的經驗,我注意到初始掛載命名空間 ID 總是獲得第一個動態值 (
0xF0000000
)。假設這是由於初始 PID 命名空間實例化了通用proc
文件系統,因此也引入了第一個掛載命名空間。無論如何,初始掛載命名空間的 ID 似乎很容易預測,即使在動態範圍內也幾乎是固定的。網路命名空間
另一方面,當配置更改影響動態生成的 inode 編號的順序時,初始網路命名空間 ID 會獲得遙遠的值,甚至可能與具有相同作業系統的同一機器的先前引導不同。因此,檢測初始網路命名空間可能成為真正的彩票。您可以經常“贏得”它,但這需要假設幾件事,儘管它們在常見的理智設置中成立,但它們不一定總是成立。
第一個網路命名空間是作為系統啟動後請求第一個網路操作的任何程序(通常是 PID 1)的結果而實例化的。因此,一個
/proc/net/
目錄變得可用,並且在其中創建文件/目錄,每個目錄都有自己的 inode 編號,這些編號是從用於命名空間 ID 的相同(動態)值分配的。碰巧(根據我撰寫本文時的經驗)其中創建的第一個名稱是stat
目錄。因此,該目錄採用在網路命名空間實例化之前立即生成的**最後一個inode 編號。因此,網路命名空間自己的 ID 是的 inode 編號 + 1。/proc/net/stat
自然地,
/proc/net/stat
目錄,實際上是一個“命名空間”名稱本身,如任意程序所見,可能不一定指初始網路命名空間。當訪問該目錄的程序存在於初始名稱空間中時(即它是非容器化程序),它確實引用了初始網路名稱空間,但在容器化環境中更可能引用該程序所屬的專用網路名稱空間,而不是而不是初始的網路命名空間。**問:**那麼程序如何嘗試一般性地猜測其網路命名空間是否實際上是初始命名空間?
答:通過遞歸列舉其目錄中所有可見的非 pid文件/目錄
/proc
來查找 inode 編號,從開始0xF0000001
直到遇到至少2 個缺失 inode 編號的第一個孔。許多非 pid 文件/目錄
/proc
(迄今為止)對所有 PID 名稱空間都是通用的,因為它們與核心的核心功能(例如 irq 統計資訊等)有關。它們的 inode 編號中的孔必須至少有2 個相鄰的數字,因為一個用於/proc/net/stat
為初始網路命名空間創建的目錄,一個用於初始網路命名空間本身(也假設兩者之間的原子分配)。在這樣的第一個洞中,將存在初始網路名稱空間的 ID。將那個(洞)ID 與程序自己的(或其他任意的)網路命名空間的 ID 進行比較,並且(在最常見的情況下)你最終都設置好了。然而,即使在常見情況下,很明顯我們依賴於那些對所有PID 命名空間始終可見的非 pid 名稱,並與命名空間的 ID 共享相同的編號,並且處於(幾乎)完美的順序和分配的inode 編號以原子方式將命名空間自己的 ID 組合在一起。所有這些假設現在可能成立,但很可能在未來不再成立,僅僅是因為這種行為根本不是官方 API。
/proc/net/*
此外,為了進一步指出這件事有多棘手,請注意,查看
/proc
的始終是掛載該特定/proc
目錄的程序的 PID 命名空間,因此不一定是讀取該/proc
目錄的程序的 PID 命名空間。/proc
在理智的實踐中不太可能出現“安裝者”和“閱讀者”之間的差異/proc
,但仍然完全有可能並且很容易導致不一致的分析。一些自以為是的考慮
除了最初的使用者命名空間,它的檢測非常容易1並且也是官方 API 的一部分,檢測命名空間是一個需要大量努力解決的問題,如果可能的話,因為沒有一個真正全面的 API 支持它(據說是為了更全面的隔離而故意的)。幾年前,一些
ioctl(2)
操作已添加到 namespaces 列表中,但它們仍然非常有限,我無法理解將它們用於確定檢測目的的任何方式(甚至不是瘋狂的方式)。確實還有一些其他簡單的技巧可以檢測 PID 命名空間,但它們也不是官方 API。例如,看看
systemd
人們最近也討論了他們的工具。顯然,他們也曾探索過關於proc
設備編號為“3 或 4”的事情,但放棄了這個想法,因為他們注意到它並沒有那麼多(也許它只在“晴天”條件下持有,無論多麼常見)他們可能是)。他們還探索了 PID 2 始終[kthreadd]
和/或整體存在核心執行緒,這將是初始 PID 命名空間的無可爭辯的標誌,但他們也放棄了這個想法,因為安裝proc
會hidepid=[12]
完全違反該檢查。我想說檢測命名空間的根本問題是它們本質上是任意的,並且可以完全被其他命名空間所取代。對於所有命名空間類型,核心確實具有所謂的“初始”命名空間,但第一個 PID 1 程序(甚至是 in 中的一個)可能會選擇通過在開始之前簡單地 -ing 所有(甚至只是幾個)
initramfs
來覆蓋它們unshare(2)
任何其他過程。顯然在這種(不是太)假設條件下,檢測初始命名空間的探索失去了任何有用的意義,因為它是相關的“主機”命名空間。這些是裸作業系統(即主PID 1init
程序)執行的命名空間一旦引導,即使就核心而言,這樣的“主機”命名空間可能已經是*子命名空間。*我並不是說init
程序真的一直覆蓋初始命名空間,但原則上它們可以,這足以削弱任何命名空間檢測工具。在我看來,對於大多數實際案例,您對任意命名空間並不**真正感興趣。幾乎可以肯定,您對 UTS、IPC、cgroup 和 time 命名空間根本不感興趣,甚至可能對 user 和 PID 命名空間都不感興趣。如果有的話,您可能只對掛載和網路名稱空間感興趣,因為它們是訪問數據和連接的相關名稱。PID 命名空間經常被尋找,只是因為 PID 命名空間(遠遠超過使用者命名空間)通常意味著更廣泛意義上的容器,而“更廣泛”的容器只是帶來有趣的掛載和網路命名空間。不幸的是,後者是最難找到的,這可能就是為什麼檢測工具更喜歡尋找 PID 命名空間,希望在它與掛載/網路命名空間之間建立鬆散但良好的關係。
總而言之,所有這些“如果”和“但是”和警告,問題在於嘗試檢測“初始或子”名稱空間的努力是否值得。我敢說它通常不會,您可能最好根本不檢測它們,或者只檢測您對特定定義明確的案例的“主機”命名空間的縮小定義。
高溫高壓
1.只需讀取
/proc/self/uid_map
文件,看看它是否0 0 4294967295
準確報告