溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

詳解Linux Namespace之User

發(fā)布時(shí)間:2020-09-11 10:33:01 來(lái)源:腳本之家 閱讀:185 作者:sparkdev 欄目:服務(wù)器

User namespace 是 Linux 3.8 新增的一種 namespace,用于隔離安全相關(guān)的資源,包括 user IDs and group IDs,keys, 和 capabilities。同樣一個(gè)用戶(hù)的 user ID 和 group ID 在不同的 user namespace 中可以不一樣(與 PID nanespace 類(lèi)似)。換句話(huà)說(shuō),一個(gè)用戶(hù)可以在一個(gè) user namespace 中是普通用戶(hù),但在另一個(gè) user namespace 中是超級(jí)用戶(hù)。

User namespace 可以嵌套(目前內(nèi)核控制最多32層),除了系統(tǒng)默認(rèn)的 user namespace 外,所有的 user namespace 都有一個(gè)父 user namespace,每個(gè) user namespace 都可以有零到多個(gè)子 user namespace。 當(dāng)在一個(gè)進(jìn)程中調(diào)用 unshare 或者 clone 創(chuàng)建新的 user namespace 時(shí),當(dāng)前進(jìn)程原來(lái)所在的 user namespace 為父 user namespace,新的 user namespace 為子 user namespace。

說(shuō)明:本文的演示環(huán)境為 ubuntu 16.04。

創(chuàng)建 user namespace

我們可以通過(guò) unshare 命令的 --user 選項(xiàng)來(lái)創(chuàng)建新的 user namespace:

$ unshare -user -r /bin/bash

詳解Linux Namespace之User

通過(guò) -r 參數(shù),我們把新的 user namespace 中的 root 用戶(hù)映射到了外面的 nick 用戶(hù)(接下來(lái)會(huì)介紹映射相關(guān)的概念)。在新的 user namespace 中,root 用戶(hù)是有權(quán)限創(chuàng)建其它的 namespace 的,比如 uts namespace。這是因?yàn)楫?dāng)前的 bash 進(jìn)程擁有全部的 capabilities:

詳解Linux Namespace之User

下面我們創(chuàng)建一個(gè)新的 uts namespace 試試:

$ unshare --uts /bin/bash

詳解Linux Namespace之User

我們看到,新的 uts namespace 被順利的創(chuàng)建了。這是因?yàn)槌?user namespace 外,創(chuàng)建其它類(lèi)型的 namespace 都需要 CAP_SYS_ADMIN 的 capability。當(dāng)新的 user namespace 創(chuàng)建并映射好 uid、gid 了之后, 這個(gè) user namespace 的第一個(gè)進(jìn)程將擁有完整的所有 capabilities,意味著它就可以創(chuàng)建新的其它類(lèi)型 namespace。

其實(shí)沒(méi)有必要把上面的操作(創(chuàng)建兩個(gè) namespace)分成兩步,我們可以通 unshare 一次創(chuàng)建多個(gè) namespace:

詳解Linux Namespace之User

在 unshare 的實(shí)現(xiàn)中,其實(shí)就是傳入了 CLONE_NEWUSER | CLONE_NEWUTS,大致如下:

unshare(CLONE_NEWUSER | CLONE_NEWUTS);

在上面這種情況下,內(nèi)核會(huì)保證 CLONE_NEWUSER 先被執(zhí)行,然后執(zhí)行剩下的其他 CLONE_NEW*,這樣就使得不用 root 用戶(hù)而創(chuàng)建新的容器成為可能,這條規(guī)則對(duì)于clone 函數(shù)也同樣適用。

理解 UID 和 GID 的映射

在前面的演示中我們提到了用戶(hù)在 user namespace 之間的映射,下面我們同樣通過(guò)演示來(lái)理解映射是什么。我們先查看下當(dāng)前用戶(hù)的 ID 和 user namespace 情況:

詳解Linux Namespace之User

然后執(zhí)行 unshare --user /bin/bash 命令創(chuàng)建一個(gè)新的 user namespace,注意這次沒(méi) -r 參數(shù):

$ unshare --user /bin/bash

詳解Linux Namespace之User

在新的 user namespace 中,當(dāng)前用戶(hù)變成了 nobody,并且 ID 也變成了 65534。

這是因?yàn)槲覀冞€沒(méi)有映射父 user namespace 的 user ID 和 group ID 到子 user namespace 中來(lái),這一步是必須的,因?yàn)檫@樣系統(tǒng)才能控制一個(gè) user namespace 里的用戶(hù)在其他 user namespace 中的權(quán)限(比如給其它 user namespace 中的進(jìn)程發(fā)送信號(hào),或者訪(fǎng)問(wèn)屬于其它 user namespace 掛載的文件)。

如果沒(méi)有映射,當(dāng)在新的 user namespace 中用 getuid() 和 getgid() 獲取 user ID 和 group ID 時(shí),系統(tǒng)將返回文件 /proc/sys/kernel/overflowuid 中定義的 user ID 以及 proc/sys/kernel/overflowgid 中定義的 group ID,它們的默認(rèn)值都是 65534。也就是說(shuō)如果沒(méi)有指定映射關(guān)系的話(huà),會(huì)默認(rèn)會(huì)把 ID 映射到 65534。

下面我們來(lái)完成 nick 用戶(hù)在新的 user namespace 中的映射。

映射 ID 的方法就是添加映射信息到 /proc/PID/uid_map 和 /proc/PID/gid_map (這里的 PID 是新 user namespace 中的進(jìn)程 ID,剛開(kāi)始時(shí)這兩個(gè)文件都是空的)文件中。這兩個(gè)文件中的配置信息的格式如下(每個(gè)文件中可以有多條配置信息):

ID-inside-ns ID-outside-ns length

比如 0 1000 500 這條配置就表示父 user namespace 中的 1000~1500 映射到新 user namespace 中的 0~500。

對(duì) uid_map 和 gid_map 文件的寫(xiě)入操作有著嚴(yán)格的權(quán)限控制,簡(jiǎn)單點(diǎn)說(shuō)就是:這兩個(gè)文件的擁有者是創(chuàng)建新的 user namespace 的用戶(hù),所以和這個(gè)用戶(hù)在一個(gè) user namespace 中的 root 賬號(hào)可以寫(xiě);這個(gè)用戶(hù)自己是否有寫(xiě) map 文件的權(quán)限還要看它有沒(méi)有 CAP_SETUID 和 CAP_SETGID 的 capability。注意:只能向 map 文件寫(xiě)一次數(shù)據(jù),但可以一次寫(xiě)多條,并且最多只能 5 條。

我們把剛才打開(kāi)的 shell 窗口稱(chēng)為第一個(gè) shell 窗口開(kāi)始執(zhí)行用戶(hù)的映射操作(把用戶(hù) nick 映射為新 user namespace 中的 root)。

第一步,先在第一個(gè) shell 窗口中查看當(dāng)前進(jìn)程的 ID:

詳解Linux Namespace之User

第二步,新打開(kāi)一個(gè) shell 窗口,我稱(chēng)之為第二個(gè) shell 窗口。查看進(jìn)程 3049 的映射文件屬性:

詳解Linux Namespace之User

用戶(hù) nick 是這兩個(gè)文件的所有者,讓我們嘗試向這兩個(gè)文件寫(xiě)入映射信息:

詳解Linux Namespace之User

看上去很奇怪呀,明明是文件的所有者,卻沒(méi)有權(quán)限向文件中寫(xiě)入內(nèi)容!其實(shí)根本的原因在于當(dāng)前的 bash 進(jìn)程沒(méi) CAP_SETUID 和 CAP_SETGID 的權(quán)限:

詳解Linux Namespace之User

下面我們?yōu)?/bin/bash 程序設(shè)置相關(guān)的 capabilities:

復(fù)制代碼 代碼如下:
$ sudo setcap cap_setgid,cap_setuid+ep /bin/bash

$ sudo setcap cap_setgid,cap_setuid+ep /bin/bash

然后重新加載 bash,就可以看到相應(yīng)的 capabilities 了:

詳解Linux Namespace之User

現(xiàn)在重新向 map 文件寫(xiě)入映射信息:

$ echo '0 1000 500' > /proc/3049/uid_map
$ echo '0 1000 500' > /proc/3049/gid_map

這次的寫(xiě)入成功了。后面就不需要我們手動(dòng)寫(xiě)入映射信息了,所以我們通過(guò)下面的命令把 /bin/bash 的 capability 恢復(fù)為原來(lái)的設(shè)置:

$ sudo setcap cap_setgid,cap_setuid-ep /bin/bash

詳解Linux Namespace之User

第三步,回到第一個(gè) shell 窗口

重新加載 bash,并執(zhí)行 id 命令:

詳解Linux Namespace之User

當(dāng)前用戶(hù)已經(jīng)變成了 root(新的 user namespace 中的 root 用戶(hù))。在看看當(dāng)前 bash 進(jìn)程具有的 capability:

詳解Linux Namespace之User

0000003fffffffff 表示當(dāng)前運(yùn)行的 bash 擁有所有的 capability。

第四步,在第一個(gè) shell 窗口中

查看 /root 目錄的訪(fǎng)問(wèn)權(quán)限:

詳解Linux Namespace之User

沒(méi)權(quán)限??!嘗試修改主機(jī)的名稱(chēng):

詳解Linux Namespace之User

依然是沒(méi)有權(quán)限啊!看來(lái)這個(gè)新 user namespace 中的 root 用戶(hù)在父 user namespace 里面不好使。這也正是 user namespace 所期望達(dá)到的效果,當(dāng)訪(fǎng)問(wèn)其它 user namespace 里的資源時(shí),是以其它 user namespace 中的相應(yīng)用戶(hù)的權(quán)限來(lái)執(zhí)行的,比如這里 root 對(duì)應(yīng)父 user namespace 的用戶(hù)是 nick,所以改不了系統(tǒng)的 hostname。

普通用戶(hù) nick 沒(méi)有修改 hostname 的權(quán)限,那把默認(rèn)的 user namespace 中的 root 用戶(hù)映射為子 user namespace 中的 root 用戶(hù)后可以修改 hostname 嗎?答案是,不行!那是因?yàn)椴还茉趺从成?,?dāng)用子 user namespace 的用戶(hù)訪(fǎng)問(wèn)父 user namespace 的資源的時(shí)候,它啟動(dòng)的進(jìn)程的 capability 都為空,所以這里子 user namespace 的 root 用戶(hù)在父 user namespace 中就相當(dāng)于一個(gè)普通的用戶(hù)。

User namespace 與其它 namespace 的關(guān)系

Linux 下的每個(gè) namespace,都有一個(gè) user namespace 與之關(guān)聯(lián),這個(gè) user namespace 就是創(chuàng)建相應(yīng) namespace 時(shí)進(jìn)程所屬的 user namespace,相當(dāng)于每個(gè) namespace 都有一個(gè) owner(user namespace),這樣保證對(duì)任何 namespace 的操作都受到 user namespace 權(quán)限的控制。這也是為什么在子 user namespace 中設(shè)置 hostname 失敗的原因,因?yàn)橐薷牡?uts namespace 屬于的父 user namespace,而新 user namespace 的進(jìn)程沒(méi)有老 user namespace 的任何 capabilities。

以 uts namespace 為例,在 uts_namespace 的結(jié)構(gòu)體中有一個(gè)指向 user namespace 的指針,指向它所屬的 user namespace(筆者查看的 v4.13內(nèi)核,uts_namespace 結(jié)構(gòu)體的定義在 /include/linux/utsname.h 文件中):

詳解Linux Namespace之User

其它 namespace 的定義也是類(lèi)似的。

總結(jié)

相對(duì)其它的 namespace 而言,user namespace 稍顯復(fù)雜。這是由其功能決定的,涉及到權(quán)限管理的內(nèi)容時(shí),事情往往會(huì)變得不那么直觀(guān)。筆者在本文中也只是介紹了 user namespace 的基本概念,更多豐富有趣的內(nèi)容還有待大家自行發(fā)掘。

參考:

user namespace man page
Namespaces in operation, part 5: User namespaces
Namespaces in operation, part 6: more on user namespaces
Linux capabilities

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI