如何讓普通進程獲得 root 的洪荒之力?

2021-01-07 程序猿DD

回復「資源」獲取獨家整理的學習資料!

Linux 是一種安全的作業系統,它把所有的系統權限都賦予了一個單一的 root 用戶,只給普通用戶保留有限的權限。root 用戶擁有超級管理員權限,可以安裝軟體、允許某些服務、管理用戶等。

作為普通用戶,如果想執行某些只有管理員才有權限的操作,以前只有兩種辦法:一是通過sudo提升權限,如果用戶很多,配置管理和權限控制會很麻煩;二是通過 SUID(Set User ID on execution)來實現,它可以讓普通用戶允許一個owner為 root 的可執行文件時具有 root 的權限。

SUID的概念比較晦澀難懂,舉個例子就明白了,以常用的passwd命令為例,修改用戶密碼是需要 root 權限的,但普通用戶卻可以通過這個命令來修改密碼,這就是因為/bin/passwd被設置了SUID標識,所以普通用戶執行 passwd 命令時,進程的 owner 就是 passwd 的所有者,也就是 root 用戶。

SUID雖然可以解決問題,但卻帶來了安全隱患。當運行設置了SUID的命令時,通常只是需要很小一部分的特權,但是SUID給了它 root 具有的全部權限。這些可執行文件是黑客的主要目標,如果他們發現了其中的漏洞,就很容易利用它來進行安全攻擊。簡而言之,SUID機制增大了系統的安全攻擊面。

為了對 root 權限進行更細粒度的控制,實現按需授權,Linux 引入了另一種機制叫capabilities。

雙十一虛擬機大促,主打機型大橫評!

01

Linux capabilities 是什麼?

Capabilities機制是在 Linux 內核2.2之後引入的,原理很簡單,就是將之前與超級用戶 root(UID=0)關聯的特權細分為不同的功能組,Capabilites 作為線程(Linux 並不真正區分進程和線程)的屬性存在,每個功能組都可以獨立啟用和禁用。其本質上就是將內核調用分門別類,具有相似功能的內核調用被分到同一組中。

這樣一來,權限檢查的過程就變成了:在執行特權操作時,如果線程的有效身份不是 root,就去檢查其是否具有該特權操作所對應的 capabilities,並以此為依據,決定是否可以執行特權操作。

Capabilities 可以在進程執行時賦予,也可以直接從父進程繼承。所以理論上如果給 nginx 可執行文件賦予了CAP_NET_BIND_SERVICEcapabilities,那麼它就能以普通用戶運行並監聽在 80 埠上。

02

capabilities 的賦予和繼承

Linux capabilities 分為進程 capabilities 和文件 capabilities。對於進程來說,capabilities 是細分到線程的,即每個線程可以有自己的capabilities。對於文件來說,capabilities 保存在文件的擴展屬性中。

下面分別介紹線程(進程)的 capabilities 和文件的 capabilities。

線程的 capabilities

每一個線程,具有 5 個 capabilities 集合,每一個集合使用64位掩碼來表示,顯示為16進位格式。這 5 個 capabilities 集合分別是:

PermittedEffectiveInheritableBoundingAmbient每個集合中都包含零個或多個 capabilities。這5個集合的具體含義如下:

Permitted

定義了線程能夠使用的 capabilities 的上限。它並不使能線程的 capabilities,而是作為一個規定。也就是說,線程可以通過系統調用capset()來從Effective或Inheritable集合中添加或刪除 capability,前提是添加或刪除的 capability 必須包含在Permitted集合中(其中 Bounding 集合也會有影響,具體參考下文)。如果某個線程想向Inheritable集合中添加或刪除 capability,首先它的Effective集合中得包含CAP_SETPCAP這個 capabiliy。

Effective

內核檢查線程是否可以進行特權操作時,檢查的對象便是Effective集合。如之前所說,Permitted集合定義了上限,線程可以刪除 Effective 集合中的某 capability,隨後在需要時,再從 Permitted 集合中恢復該 capability,以此達到臨時禁用 capability 的功能。

Inheritable

當執行exec()系統調用時,能夠被新的可執行文件繼承的 capabilities,被包含在Inheritable集合中。這裡需要說明一下,包含在該集合中的 capabilities 並不會自動繼承給新的可執行文件,即不會添加到新線程的Effective集合中,它只會影響新線程的Permitted集合。

Bounding

Bounding集合是Inheritable集合的超集,如果某個 capability 不在Bounding集合中,即使它在Permitted集合中,該線程也不能將該 capability 添加到它的Inheritable集合中。

Bounding 集合的 capabilities 在執行fork()系統調用時會傳遞給子進程的 Bounding 集合,並且在執行execve系統調用後保持不變。

當線程運行時,不能向 Bounding 集合中添加 capabilities。一旦某個 capability 被從 Bounding 集合中刪除,便不能再添加回來。將某個 capability 從 Bounding 集合中刪除後,如果之前Inherited集合包含該 capability,將繼續保留。但如果後續從Inheritable集合中刪除了該 capability,便不能再添加回來。Ambient

Linux4.3內核新增了一個 capabilities 集合叫Ambient,用來彌補Inheritable的不足。Ambient具有如下特性:

Permitted和Inheritable未設置的 capabilities,Ambient也不能設置。當Permitted和Inheritable關閉某權限(比如CAP_SYS_BOOT)後,Ambient也隨之關閉對應權限。這樣就確保了降低權限後子進程也會降低權限。非特權用戶如果在Permitted集合中有一個 capability,那麼可以添加到Ambient集合中,這樣它的子進程便可以在Ambient、Permitted和Effective集合中獲取這個 capability。現在不知道為什麼也沒關係,後面會通過具體的公式來告訴你。Ambient的好處顯而易見,舉個例子,如果你將CAP_NET_ADMIN添加到當前進程的Ambient集合中,它便可以通過fork()和execve()調用 shell 腳本來執行網絡管理任務,因為CAP_NET_ADMIN會自動繼承下去。

文件的 capabilities

文件的 capabilities 被保存在文件的擴展屬性中。如果想修改這些屬性,需要具有CAP_SETFCAP的 capability。文件與線程的 capabilities 共同決定了通過execve()運行該文件後的線程的 capabilities。

文件的 capabilities 功能,需要文件系統的支持。如果文件系統使用了nouuid選項進行掛載,那麼文件的 capabilities 將會被忽略。

類似於線程的 capabilities,文件的 capabilities 包含了 3 個集合:

PermittedInheritableEffective這3個集合的具體含義如下:

Permitted

這個集合中包含的 capabilities,在文件被執行時,會與線程的 Bounding 集合計算交集,然後添加到線程的Permitted集合中。

Inheritable

這個集合與線程的Inheritable集合的交集,會被添加到執行完execve()後的線程的Permitted集合中。

Effective

這不是一個集合,僅僅是一個標誌位。如果設置開啟,那麼在執行完execve()後,線程Permitted集合中的 capabilities 會自動添加到它的Effective集合中。對於一些舊的可執行文件,由於其不會調用 capabilities 相關函數設置自身的Effective集合,所以可以將可執行文件的 Effective bit 開啟,從而可以將Permitted集合中的 capabilities 自動添加到Effective集合中。

詳情請參考 Linux capabilities 的 man page。

03

運行 execve() 後 capabilities 的變化

上面介紹了線程和文件的 capabilities,你們可能會覺得有些抽象難懂。下面通過具體的計算公式,來說明執行execve()後 capabilities 是如何被確定的。

我們用P代表執行execve()前線程的 capabilities,P'代表執行execve()後線程的 capabilities,F代表可執行文件的 capabilities。那麼:

P'(ambient) = (file is privileged) ? 0 : P(ambient)P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & P(bounding))) | P'(ambient)P'(effective) = F(effective) ? P'(permitted) : P'(ambient)P'(inheritable) = P(inheritable) [i.e., unchanged]P'(bounding) = P(bounding) [i.e., unchanged]

我們一條一條來解釋:

如果用戶是 root 用戶,那麼執行execve()後線程的Ambient集合是空集;如果是普通用戶,那麼執行execve()後線程的Ambient集合將會繼承執行execve()前線程的Ambient集合。執行execve()前線程的Inheritable集合與可執行文件的Inheritable集合取交集,會被添加到執行execve()後線程的Permitted集合;可執行文件的 capability bounding 集合與可執行文件的Permitted集合取交集,也會被添加到執行execve()後線程的Permitted集合;同時執行execve()後線程的Ambient集合中的 capabilities 會被自動添加到該線程的Permitted集合中。如果可執行文件開啟了 Effective 標誌位,那麼在執行完execve()後,線程Permitted集合中的 capabilities 會自動添加到它的Effective集合中。執行execve()前線程的Inheritable集合會繼承給執行execve()後線程的Inheritable集合。這裡有幾點需要著重強調:

上面的公式是針對系統調用execve()的,如果是fork(),那么子線程的 capabilities 信息完全複製父進程的 capabilities 信息。可執行文件的Inheritable集合與線程的Inheritable集合併沒有什麼關係,可執行文件Inheritable集合中的 capabilities 不會被添加到執行execve()後線程的Inheritable集合中。如果想讓新線程的Inheritable集合包含某個 capability,只能通過capset()將該 capability 添加到當前線程的Inheritable集合中(因為 P'(inheritable) = P(inheritable))。如果想讓當前線程Inheritable集合中的 capabilities 傳遞給新的可執行文件,該文件的Inheritable集合中也必須包含這些 capabilities(因為 P'(permitted) = (P(inheritable) & F(inheritable))|...)。將當前線程的 capabilities 傳遞給新的可執行文件時,僅僅只是傳遞給新線程的Permitted集合。如果想讓其生效,新線程必須通過capset()將 capabilities 添加到Effective集合中。或者開啟新的可執行文件的 Effective 標誌位(因為 P'(effective) = F(effective) ? P'(permitted) : P'(ambient))。在沒有Ambient集合之前,如果某個腳本不能調用capset(),但想讓腳本中的線程都能獲得該腳本的Permitted集合中的 capabilities,只能將Permitted集合中的 capabilities 添加到Inheritable集合中(P'(permitted) = P(inheritable) & F(inheritable)|...),同時開啟 Effective 標誌位(P'(effective) = F(effective) ? P'(permitted) : P'(ambient))。有 有Ambient集合之後,事情就變得簡單多了,後續的文章會詳細解釋。如果某個 UID 非零(普通用戶)的線程執行了execve(),那麼Permitted和Effective集合中的 capabilities 都會被清空。從 root 用戶切換到普通用戶,那麼Permitted和Effective集合中的 capabilities 都會被清空,除非設置了 SECBIT_KEEP_CAPS 或者更寬泛的 SECBIT_NO_SETUID_FIXUP。關於上述計算公式的邏輯流程圖如下所示(不包括Ambient集合):

04

簡單示例

下面我們用一個例子來演示上述公式的計算邏輯,以ping文件為例。如果我們將CAP_NET_RAW capability添加到 ping 文件的Permitted集合中(F(Permitted)),它就會添加到執行後的線程的Permitted集合中(P'(Permitted))。由於 ping 文件具有capabilities 意識,即能夠調用capset()和capget(),它在運行時會調用capset()將CAP_NET_RAWcapability 添加到線程的Effective集合中。

換句話說,如果可執行文件不具有capabilities 意識,我們就必須要開啟 Effective 標誌位(F(Effective)),這樣就會將該 capability 自動添加到線程的Effective集合中。具有capabilities 意識的可執行文件更安全,因為它會限制線程使用該 capability 的時間。

我們也可以將 capabilities 添加到文件的Inheritable集合中,文件的Inheritable集合會與當前線程的Inheritable集合取交集,然後添加到新線程的Permitted集合中。這樣就可以控制可執行文件的運行環境。

看起來很有道理,但有一個問題:如果可執行文件的有效用戶是普通用戶,且沒有Inheritable集合,即F(inheritable) = 0,那麼P(inheritable)將會被忽略(P(inheritable) & F(inheritable))。由於絕大多數可執行文件都是這種情況,因此Inheritable集合的可用性受到了限制。我們無法讓腳本中的線程自動繼承該腳本文件中的 capabilities,除非讓腳本具有capabilities 意識。

要想改變這種狀況,可以使用Ambient集合。Ambient集合會自動從父線程中繼承,同時會自動添加到當前線程的Permitted集合中。舉個例子,在一個 Bash 環境中(例如某個正在執行的腳本),該環境所在的線程的Ambient集合中包含CAP_NET_RAWcapability,那麼在該環境中執行 ping 文件可以正常工作,即使該文件是普通文件(沒有任何 capabilities,也沒有設置 SUID)。

05

終極案例

最後拿 docker 舉例,如果你使用普通用戶來啟動官方的 nginx 容器,會出現以下錯誤:

bind() to 0.0.0.0:80 failed (13: Permission denied)因為 nginx 進程的Effective集合中不包含CAP_NET_BIND_SERVICEcapability,且不具有capabilities 意識(普通用戶),所以啟動失敗。要想啟動成功,至少需要將該 capability 添加到 nginx 文件的Inheritable集合中,同時開啟 Effective 標誌位,並且在 Kubernetes Pod 的部署清單中的 securityContext --> capabilities 欄位下面添加NET_BIND_SERVICE(這個 capability 會被添加到 nginx 進程的Bounding集合中),最後還要將 capability 添加到 nginx 文件的Permitted集合中。如此一來就大功告成了,參考公式:P'(permitted) = ...|(F(permitted) & P(bounding)))|...和P'(effective) = F(effective) ? P'(permitted) : P'(ambient)。

如果容器開啟了securityContext/allowPrivilegeEscalation,上述設置仍然可以生效。如果 nginx 文件具有capabilities 意識,那麼只需要將CAP_NET_BIND_SERVICEcapability 添加到它的Inheritable集合中就可以正常工作了。

當然了,除了上述使用文件擴展屬性的方法外,還可以使用Ambient集合來讓非 root 容器進程正常工作,但 Kubernetes 目前還不支持這個屬性,具體參考 Kubernetes 項目的 issue。

雖然 Kubernetes 官方不支持,但我們可以自己來實現,具體實現方式可以關注我後續的文章。

06

參考資料

Linux Capabilities: Why They Exist and How They WorkUnderstanding Capabilities in LinuxLinux Capabilities in a nutshellLinux的capabilities機制

雙十一虛擬機大促,主打機型大橫評!

本文通過OpenWrite的免費Markdown轉換工具發布

留言交流不過癮

關注我,回復「加群」加入各種主題討論群

從零搭建創業公司後臺技術棧別死寫代碼,這 25 條比漲工資都重要效率至上:K8S一鍵部署了解一下?Java 14 可能帶來什麼新特性?面試最後一問:你有什麼問題想問我嗎?

朕已閱

相關焦點

  • 洪荒之力是什麼意思
    洪荒之力是什麼意思,估計很多人是看了熱播劇和傅園慧在奧運賽場上的採訪,現在很多年輕人想要表達自己要做出成績做出表現或者你已經取得非常好的成績後的話語——我用的是洪荒之力!個人是80後,在我們小的時候,經常會說要爆發我的小宇宙,大家知道是源於何嗎?
  • 雅思新詞彙 外媒如何翻譯「洪荒之力」?
    編者按:英國廣播公司(BBC)、澳洲「news.com.au」等將「洪荒之力
  • 【譯事帖】如何翻譯「洪荒之力」
    那句令人忍俊不禁的「我已經用上我的洪荒之力了」更是被媒體廣為引用。相對於博大精深的中文表達,外媒顯然難以找到合適的語句來翻譯「洪荒之力」。這個詞自去年電視劇《花千骨》播出後便開始在年輕人中流行,在劇中指能夠毀天滅地的妖神之力。
  • 「洪荒之力」火了,英語怎麼說?
    在裡約奧運第二天結束的女子100米仰泳半決賽中,中國選手傅園慧以58秒95的成績獲得第三,晉級決賽。賽後,傅園慧在接受記者採訪時表示:對於這次比賽,自己沒有保留實力,已經用了「洪荒之力」。於是「洪荒之力」一詞迅速走紅。
  • 「洪荒之力」怎麼翻譯?
    她在賽後接受採訪時說的「我已經用了洪荒之力!」也迅速走紅,成為了時下熱潮新詞...諸如:「小王,今年上半年的銷售業績怎麼樣?」「我已經盡了洪荒之力!」and「老公,你覺得你對我夠好了嗎?」「我已經盡了洪荒之力!!」
  • 「洪荒之力」用英語怎麼說?
    ↓↓↓「對於這次比賽,自己沒有保留實力,已經用了『洪荒之力』!」傅寶寶的耿直採訪讓「洪荒之力」一詞迅速走紅。這個「洪荒之力」用英語怎麼表達呢?這裡用prehistorical powers來表達「洪荒之力」,從字面上直接反應了地球形成早期,也就是「史前」這個概念。如果按照傅園慧接受採訪的語境,她說半決賽沒有保留,「已經用了洪荒之力」,其實就是「不遺餘力、用盡全力」的意思。
  • 如何用英語說「我已經用了洪荒之力啦」?
    傅園慧:沒有,我已經用了洪荒之力啦!記者:記者:對明天有沒有充滿期望?傅園慧:沒有,我已經很心滿意足了記者:.天地初開之時稱為洪荒,傳說天地初開之時,曾經有過一次大洪水,幾乎毀滅了整個世界。因此,洪荒之力指的是如同洪荒世界之時,這種足以毀滅世界的力量。那麼「洪荒之力」英文到底怎麼說?媒體上也吵得沸沸揚揚。我們來看看各大中外媒體對「洪荒之力」的說法。
  • 產房裡的洪荒之力
    圖丨愛的能量環為什麼有的產婦同樣在助產士的指導下,用了洪荒之力也沒有達到預期的效果呢?而很多產婦過早的運用「洪荒之力」,在產床上情緒失控,大喊大叫,胡踢亂蹬,這樣只會過多的消耗體力,同時沒有有效的呼吸,容易導致寶寶在宮內缺氧,胎心過快或過慢。因此,產婦良好的情緒,必要的呼吸和軀體放鬆是非常重要的。News那如何正確的運用腹壓,達到滿意的產力呢?
  • 最新「成語」洪荒之力到底是個啥意思?
    尤其是這句「洪荒之力」,令人印象深刻。  但疑問也來了:這個最新「成語」洪荒之力到底是啥意思?它有多厲害呢?今天就給大家「全方位、多角度」科普一下。  「洪荒之力」打哪來?  沒想到,遊泳界的「泥石流」不僅是個段子手,還很愛追劇。
  • 細思恐極:「洪荒之力」到底是一股什麼力量?
    當然,和她一起沸騰起來的,還有 「洪荒之力」一詞。那麼洪荒之力到底是什麼力?有人用它來形容自己脾氣大,叫「控制不住自己體內的洪荒之力」;有人用它來描述食慾,「無法控制自己吃吃吃的洪荒之力」,等等。影視劇中妖神南弦月的真身幻滅,能量被封印花千骨體內,這股能量就叫做「洪荒之力」。後來,經歷了生離死別的花千骨把體內的「洪荒之力」統統釋放了出來,具有毀滅天地的力量。
  • 洪荒之力爆發 看門窗企業如何玩轉終端渠道建設
    裡約奧運賽事還在火爆進行中,遊泳小將傅園慧的金句「我已經用了洪荒之力」早已刷爆朋友圈,其率真的表達和感情流露也引發了門窗行業的關注。受網際網路電商衝擊與門窗品牌數量激增的影響,門窗市場競爭也日趨激烈。在這樣的市場環境下,各大門窗品牌紛紛想盡辦法通過舉辦各類招商、培訓活動提升線下渠道的核心競爭能力。
  • 我已使出洪荒之力咋翻譯?央視給標準答案
    緊張激烈的裡約奧運賽場上,中國遊泳女隊的傅園慧卻憑藉逗X之際的表情火爆全球,尤其是那句「我已經使出了洪荒之力」讓人頓時失去了招架之力。  奧運會是個國際大舞臺,那麼這個「洪荒之力」該如何向全世界的網友們介紹呢?英語裡怎麼說?
  • 什麼讓傅園慧說出「洪荒之力」四個字,博覽群書是她安靜的另一面
    說到傅園慧,大家一定都不陌生,那個「洪荒之力」的「行走的表情包」說的就是她。·體弱多病進體校1996年出生在杭州一個普通家庭的傅園慧,自小就體弱多病,而且還患有哮喘病。5歲時,父母將傅園慧送進了體校,希望她可以通過學習遊泳增強體質,減少哮喘病的復發。
  • 堪比傅園慧 寶島少女使出「洪荒之力」踢館
    最近,奧運網紅傅園慧自帶表情包紅遍全球,參加半決賽後受訪時激動喊「我已經用了洪荒之力了」的可愛逗比模樣,更是在朋友圈廣為傳播,小夥伴們紛紛表示釋放洪荒之力的傅園慧萌翻天的個性範兒,真真是一種別樣的賽場之美!
  • 「洪荒之力」究竟是什麼力量 用英語怎麼說
    洪荒之力一詞相信大部分同學第一次聽說是來自於去年暑期熱播劇《花千骨》中吧,然而這個詞最近突然就頻頻出現在各媒體的標題之中,原因還是來自這兩天備受大家關注的傅園慧在賽後採訪中表示:對於這次比賽,自己沒有保留實力,已經用了「洪荒之力」。
  • 洪荒之力來襲:那些力量的英文表達怎麼說?
    新東方網>英語>英語學習>語法詞彙>詞彙指導>正文洪荒之力來襲:那些力量的英文表達怎麼說? 2016-08-17 10:19 來源:滬江 作者:   看著奧運會中運動員們各自拿出「洪荒之力」爭奪獎牌時,你有沒有也想爆發體內的「洪荒之力」來學習英語了呢?
  • #經驗榜#《媽媽發火了》,送給控制不住洪荒之力的你
    當我的孩子不聽話的時候,我體內的洪荒之力也會被激發,累計到一定程度的時候,也會暴走。所以,這本媽媽發火了,推薦給控制不住自己體內洪荒之力的你我。 我發火的時候,我的孩子會怎麼樣呢? 這三幅圖,媽媽洪荒之力爆發出來,慢慢將寶寶吞沒,寶寶吃驚、害怕、不知所措。寶寶的消失,大概就是他不知道下一步該如何行動的心理狀態吧。
  • 策展人:洪荒之力應用觀念上
    (原標題:策展人:洪荒之力應用觀念上) 京華時報訊
  • 「洪荒之力」究竟是什麼力量?用英語咋說?
    【MBA中國網訊】洪荒之力一詞相信大部分同學第一次聽說是來自於去年暑期熱播劇《花千骨》中吧,然而這個詞最近突然就頻頻出現在各媒體的標題之中,原因還是來自最近備受大家關注的傅園慧在賽後採訪中表示:對於這次比賽,自己沒有保留實力,已經用了「洪荒之力」。
  • 教育部發布2016年十大網絡用語:洪荒之力、葛優躺入選
    中新網北京7月18日消息,18日,教育部、國家語委在北京發布《中國語言生活狀況報告(2017)》,報告顯示,洪荒之力、吃瓜群眾等成為2016年中國媒體十大新詞語,而葛優躺、辣眼睛、全是套路等成為年度網絡用語。