注:本文絕大部分內容來自Linux Namespaces實踐part 6,原文系列文章詳細描述了Linux Namespace相關內容,英語過關的建議閱讀原文,本文內容主要用來記錄學習內容,如有不當之處還請評論區指正。
User Namespace 與 capabilities
- User Namespace
- 每個進程都關聯一個
User Namespace
fork
或無CLONE_NEWUSER
標誌的clone
函數創建的子進程,其User Namespace
與父進程相同- 擁有
CAP_SYS_ADMIN
的進程通過setns
函數可以更改其User Namespace
,並獲得該User Namespace
中的所有權限 clone(CLONE_NEWUSER)
使子進程運行在在新User Namespace
中,並建立相關的父子關係- 除
init
外,其它User Namespace
皆有一個父User Namespace
unshare(CLONE_NEWUSER)
也創建新User Namespace
,與clone
不同,unshare
將當前進程放在新創建的User Namespace
,新創建的Namespace
爲當前進程之前Namespace
的子代User Namespace
的父子關係決定了一個子Namespace
所擁有的的權限
- 每個進程都關聯一個
- capabilities
- 每個進程關聯三個
capabilities
集合:permitted
,effective
,inheritable
,詳見capabilities - 特定
User Namespace
中,進程只能在該Namespace
管理的資源上進行操作 - 一個進程是否有某項權限取決於其在該
Namespace
的地位以及Namespace
的父子關係,規則如下- 如果一個進程位於
Namespace
中,則該進程擁有其effective capability set
中的權限。進程可以通過多種方式獲得capabilities
,一種方式(emmm沒懂): *The most common reasons are that it executed a program that conferred capabilities (a set-user-ID program or a program that has associated file capabilities)*或者該進程是clone(CLONE_NEWUSER)
的執行函數 - 如果進程在一個
User Namespace
中擁有某項權限,那麼它的所有子Namespace
也有該權限。 - 進程創建
User Namespace
時,內核會記錄該進程的有效用戶ID(effective user ID)作爲創建的Namespace
的"所有者(owner)"。如果一個進程的有效用戶ID與一個Namespace
的相同,並且是位於其父Namespace
中,則該進程在這個Namespace
中擁有所有權限。
- 如果一個進程位於
- 每個進程關聯三個
一個栗子
本例使用了userns_setns_test.c以及userns_child_exec.c,大致過程如下:
- 使用
userns_child_exec.c
程序創建User Namespace
並在其中運行ksh
程序 - 查看步驟1創建的
ksh
的PID,得到路徑/proc/PID/ns/user/
- 將步驟2得到的路徑作爲參數傳遞給
userns_setns_test.c
,該程序會在新User Namespace
中創建子進程,並通過setns
,嘗試將父、子進程加入到ksh
的User Namespace
中
步驟及輸出:
- 在終端中執行下列命令查看輸出
$ id -u 1000 $ readlink /proc/$$/ns/user # Obtain ID for initial namespace user:[4026531837] $ ./userns_child_exec -U -M '0 1000 1' -G '0 1000 1' ksh ksh$ echo $$ # Obtain PID of shell 528 ksh$ readlink /proc/$$/ns/user # This shell is in a new namespace
- 切換到另一個終端執行如下命令查看輸出
$ readlink /proc/$$/ns/user # Verify that we are in parent namespace user:[4026531837] $ ./userns_setns_test /proc/528/ns/user parent: readlink("/proc/self/ns/user") ==> user:[4026531837] parent: setns() succeeded child: readlink("/proc/self/ns/user") ==> user:[4026532319] child: setns() failed: Operation not permitted
下圖展示了創建的User Namespace
的父子關係:
可以看到,userns_setns_test
與userns_child_exec
位於相同User Namespace
中,因此在4026531838
中擁有所有權限,而其子進程位於4026532319
中,沒有ksh
所在Namespace
的權限,因此執行setns
失敗。
User Namespace與其它Namespace的結合
- 除
User Namespace
外,單獨創建其它Namespace
需要CAP_SYS_ADMIN
- 對於使用
CLONE_NEWUSER
標誌的clone()
或unshare()
函數,在標誌中包含其它標誌如CLONE_NEWPID
時不需要CAP_SYS_ADMIN
,內核會保證CLONE_NEWUSER
先執行,之後再執行其它標誌創建對應的Namespace
。例如clone(child_func, stackp, CLONE_NEWUSER | CLONE_NEWUTS, arg);
不需要root權限
再看capabilities
- 儘管新
User Namespace
中的第一個進程擁有所有capabilities
,但該進程在整個系統中並不會具有超級用戶權限 - 當通過
clone()
或unshare()
創建一個新PID
、mount
、network
等Namespace時,內核會記錄這些Namespace
對應的User Namespace
,並據此檢查這些Namespace
對系統資源的權限- 例如,假設通過
CLONE(CLONE_NEWUSER)
創建一個新的User Namespace
,那麼 - 一方面,子進程(擁有該
Namespace
所有capabilities
,特權用戶)能夠創建其它的的Namespace
或將用戶ID或組ID更改爲其它映射到的ID - 另一方面,子進程無法掛載文件系統,因爲該進程位於父進程的
mount Namespace
中,而在該mount namespace
對應的User Namespace
中,子進程沒有相應的capabilities
,即非特權用戶 - 進一步來說,子進程無法執行一些需要額外的、其所在·Namespace·不包含的·capabilities·的操作,例如提升硬件資源限制、設置系統時間、設置進程屬性或加載內核模塊等
- 例如,假設通過
總結
通過隔離·capabilities·的影響(effect),·User Namespace·允許非特權用戶執行一些需要root身份的操作,這也給用戶空間應用程序創造了機會,例如,非特權用戶可以運行Linux containers而不需要root權限等。本文對·User Namespace·以及相關的·capabilities·作了短期的總結,接下來會介紹·network namespace·與·mount namespace·。