該系列文章總共分爲三篇:html
待續...git
上篇文章介紹了 Linux capabilities 的誕生背景和基本原理,本文將會經過具體的示例來展現如何查看和設置文件的 capabilities。github
Linux 系統中主要提供了兩種工具來管理 capabilities:libcap
和 libcap-ng
。libcap
提供了 getcap
和 setcap
兩個命令來分別查看和設置文件的 capabilities,同時還提供了 capsh
來查看當前 shell 進程的 capabilities。libcap-ng
更易於使用,使用同一個命令 filecap
來查看和設置 capabilities。docker
安裝很簡單,以 CentOS 爲例,能夠經過如下命令安裝:shell
$ yum install -y libcap
若是想查看當前 shell 進程的 capabilities,能夠用 capsh
命令。下面是 CentOS 系統中的 root 用戶執行 capsh
的輸出:安全
$ capsh --print Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read Securebits: 00/0x0/1'b0 secure-noroot: no (unlocked) secure-no-suid-fixup: no (unlocked) secure-keep-caps: no (unlocked) uid=0(root) gid=0(root) groups=0(root)
解釋一下:bash
Current : 表示當前 shell 進程的 Effective capabilities 和 Permitted capabilities。能夠包含多個分組,每個分組的表示形式爲 capability[,capability…]+(e|i|p)
,其中 e
表示 effective,i
表示 inheritable,p
表示 permitted。不一樣的分組之間經過空格隔開,例如:Current: = cap_sys_chroot+ep cap_net_bind_service+eip
。再舉一個例子,cap_net_bind_service+e cap_net_bind_service+ip
和 cap_net_bind_service+eip
等價。微信
Bounding set : 這裏僅僅表示 Bounding 集合中的 capabilities,不包括其餘集合,因此分組的末尾不用加上 +...
。
Securebits : 我也沒搞清楚這是個什麼鬼。
這個命令輸出的信息比較有限,完整的信息能夠查看 /proc 文件系統,好比當前 shell 進程就能夠查看 /proc/$$/status
。其中一個重要的狀態就是 NoNewPrivs
,能夠經過如下命令查看:
grep NoNewPrivs /proc/$$/status NoNewPrivs: 0
根據 prctl(2) 中的描述,自從 Linux 4.10 開始,/proc/[pid]/status
中的 NoNewPrivs
值表示了線程的 no_new_privs
屬性。至於 no_new_privs
到底是幹嗎的,下面我單獨解釋一下。
通常狀況下,execve()
系統調用可以賦予新啓動的進程其父進程沒有的權限,最多見的例子就是經過 setuid
和 setgid
來設置程序進程的 uid 和 gid 以及文件的訪問權限。這就給不懷好意者鑽了很多空子,能夠直接經過 fork 來提高進程的權限,從而達到不可告人的目的。
爲了解決這個問題,Linux 內核從 3.5 版本開始,引入了 no_new_privs
屬性(實際上就是一個 bit,能夠開啓和關閉),提供給進程一種可以在 execve()
調用整個階段都能持續有效且安全的方法。
開啓了 no_new_privs
以後,execve 函數能夠確保全部操做都必須調用 execve()
判斷並賦予權限後才能被執行。這就確保了線程及子線程都沒法得到額外的權限,由於沒法執行 setuid 和 setgid,也不能設置文件的權限。
一旦當前線程的 no_new_privs
被置位後,不論經過 fork,clone 或 execve 生成的子線程都沒法將該位清零。
Docker 中能夠經過參數 --security-opt
來開啓 no_new_privs
屬性,例如:docker run --security-opt=no_new_privs busybox
。下面經過一個例子來體會一下 no_new_privs
屬性的做用。
首先擼一段 C 代碼,顯示當前進程的有效用戶 id:
$ cat testnnp.c #include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(int argc, char *argv[]) { printf("Effective uid: %d\n", geteuid()); return 0; }
$ make testnnp cc testnnp.c -o testnnp
將可執行文件打入 docker 鏡像中:
FROM fedora:latest ADD testnnp /root/testnnp RUN chmod +s /root/testnnp ENTRYPOINT /root/testnnp
構建鏡像:
$ docker build -t testnnp . Step 1 : FROM fedora:latest ---> 760a896a323f Step 2 : ADD testnnp /root/testnnp ---> 6c700f277948 Removing intermediate container 0981144fe404 Step 3 : RUN chmod +s /root/testnnp ---> Running in c1215bfbe825 ---> f1f07d05a691 Removing intermediate container c1215bfbe825 Step 4 : ENTRYPOINT /root/testnnp ---> Running in 5a4d324d54fa ---> 44f767c67e30 Removing intermediate container 5a4d324d54fa Successfully built 44f767c67e30
下面來作兩個實驗,先在沒有開啓 no-new-privileges
的狀況下啓動容器:
$ docker run -it --rm --user=1000 testnnp Effective uid: 0
從輸出結果來看,只要給可執行文件設置了 SUID 標識,即便咱們使用普通用戶(UID=1000)來運行容器,進程的有效用戶也會變成 root。
接着在開啓 no-new-privileges
的前提下啓動容器,以防止執行設置了 SUID 標識的可執行文件進行 UID 轉換:
$ docker run -it --rm --user=1000 --security-opt=no-new-privileges testnnp Effective uid: 1000
能夠看到,開啓了 no_new_privs
屬性以後,即便可執行文件設置了 SUID 標識,線程的有效用戶 ID 也不會變成 root。這樣即便鏡像中的代碼有安全風險,仍然能夠經過防止其提高權限來避免受到攻擊。
Kubernetes 也能夠開啓 no_new_privs
,不過邏輯稍微複雜一點。當 Pod 的 SecurityContext
定義下的 allowPrivilegeEscalation
字段值爲 false 時(默認就是 false),若是不知足如下任何一個條件,就會開啓 no_new_privs
屬性:
設置了 privileged=true
增長了 CAP_SYS_ADMIN
capabilities,即 capAdd=CAP_SYS_ADMIN
以 root 用戶運行,即 UID=0
例如,當設置了 privileged=true
和 allowPrivilegeEscalation=false
時,就不會開啓 no_new_privs
屬性。同理,設置了 capAdd=CAP_SYS_ADMIN
和 allowPrivilegeEscalation=false
也不會開啓 no_new_privs
屬性。
能夠經過 getcap
來查看文件的 capabilities,例如:
$ getcap /bin/ping /usr/sbin/arping /bin/ping = cap_net_admin,cap_net_raw+p /usr/sbin/arping = cap_net_raw+p
也可使用 -r
參數來遞歸查詢:
$ getcap -r /usr 2>/dev/null /usr/bin/ping = cap_net_admin,cap_net_raw+p /usr/bin/newgidmap = cap_setgid+ep /usr/bin/newuidmap = cap_setuid+ep /usr/sbin/arping = cap_net_raw+p /usr/sbin/clockdiff = cap_net_raw+p
若是想查看某個進程的 capabilities,能夠直接使用 getpcaps
,後面跟上進程的 PID:
$ getpcaps 1234
若是想查看一組相互關聯的線程的 capabilities(好比 nginx),能夠這麼來看:
$ getpcaps $(pgrep nginx)
這裏你會看到只有主線程纔有 capabilities,子線程和其餘 workers 都沒有 capabilities,這是由於只有 master 才須要特殊權限,例如監聽網絡端口,其餘線程只須要響應請求就行了。
設置文件的 capabilities 可使用 setcap
,語法以下:
$ setcap CAP+set filename
例如,將 CAP_CHOWN
和 CAP_DAC_OVERRIDE
capabilities 添加到 permitted
和 effective
集合:
$ setcap CAP_CHOWN,CAP_DAC_OVERRIDE+ep file1
若是想移除某個文件的 capabilities,可使用 -r
參數:
$ setcap -r filename
安裝也很簡單,以 CentOS 爲例:
$ yum install libcap-ng-utils
libcap-ng 使用 filecap
命令來管理文件的 capabilities。有幾個須要注意的地方:
filecap 添加刪除或查看 capabilities 時,capabilities 的名字不須要帶 CAP_
前綴(例如,使用 NET_ADMIN
代替 CAP_NET_ADMIN
);
filecap 不支持相對路徑,只支持絕對路徑;
filecap 不容許指定 capabilities 做用的集合,capabilities 只會被添加到 permitted
和 effective
集合。
查看文件的 capabilities:
$ filecap /full/path/to/file
遞歸查看某個目錄下全部文件的 capabilities:
$ filecap /full/path/to/dir
例如:
$ filecap /usr/bin file capabilities /usr/bin/newgidmap setgid /usr/bin/newuidmap setuid
注意: filecap 只會顯示「capabilities 被添加到
permitted
和effective
集合中」的文件。因此這裏沒有顯示 ping 和 arping。
遞歸查看整個系統全部文件的 capabilities:
$ filecap / # or $ filecap -a
設置文件的 capabilities 語法以下:
$ filecap /full/path/to/file cap_name
例如:
$ filecap /usr/bin/tac dac_override
移除某個文件的 capabilities:
$ filecap /full/path/to/file none
本文經過兩種工具演示瞭如何對可執行文件的 capabilities 進行管理,並以 docker 爲例,展示了 no_new_privs
的強大之處。若是條件容許,推薦你們之後儘可能用 capabilities 來替代完整的 root 權限或者設置 SUID 標識。
掃一掃下面的二維碼關注微信公衆號,在公衆號中回覆◉加羣◉便可加入咱們的雲原生交流羣,和孫宏亮、張館長、陽明等大佬一塊兒探討雲原生技術
原文出處:https://www.cnblogs.com/ryanyangcs/p/11798292.html