爲什麼在Docker裏使用gdb調試器會報錯

背景

前幾天一個小夥伴發郵件問我,他在docker內部使用gdb調試時刻遇到了gdb如下報錯信息
ptrace:Operation not permitted
當時我的答覆是在docker create或者docker run時刻開啓萬精油--privileged參數。小夥伴的問題就此解決了。
但是事實並非如此簡單

Docker上涉及到gdb調試權限的特性

capabilities

Docker借用了linux對進程設置capabilities,而其子進程繼承父進程capabilites特性來完成對容器capacities的控制。Docker create和docker run參數中有下面兩個參數可以對容器默認的capabilites進行修改:
--cap-add //添加某個capabilites屬性 --cap-del //剔除某個默認的capabilites屬性
cap-add和cap-del可以設置的參數可以通過下面鏈接查詢到:
https://docs.docker.com/engine/reference/commandline/run/

  • Docker 將gdb調試需要SYS_PTRACE屬性被禁止掉了,所以gdb在調試的時候會顯示ptrace被禁止。所以想在docker內部調試gdb解決辦法就是create和run的時候帶上--cap-add sys_ptrace*
    例如:
docker run  -it --cap-add sys_ptrace centos:latest /bin/bash
root@7f5a2130e975>cd /home/
root@7fa2130e975> vi test.c
\#include <stdio.h>
int main(){
     int i = 0;
    printf(“Testing begin\n”);
    While(1){
       printf(“Loop cnt:%d\n”,i++);
       sleep(10);   
    }
 }

root@7fa2130975>gcc -c -g -o test.o test.c
root@7fa2130975>gcc -o test -g test.o
root@7fa2130975>./test&
[1] 18
root@7fa2130975>gdb attach 18
//ok可以調試了  

但是這並不是問題的全部,對於上述測試程序,如果執行下面命令gdb又有告警出來

root@7fa2130975>gdb ./test
(gdb) r
Warning:Error disabling address space randomization:Operation not permitted

雖然依然可以調試,但是我們還是需要搞清楚上述告警的意思。地址隨機化是linux一項安全特性,它允許內核進程啓動每次加載庫的時候都在隨機化的分佈在進程虛擬內存地址空間上(早期固定的庫要加載到固定地方,如果固定地方被佔用才加載到別地方。會造成多次加載程序,其庫地址都不變。如此有安全隱患)。在gdb調試中gdb默認需要關閉linux的地址隨機化功能,可以通過gdb 命令set disable-randomization off關閉。如果在地址隨機化下調試同一段程序,多次run時候可以看到它的運行地址和函數地址不一致,這沒有什麼太大的問題。問題可以結束了
關於gdb 設置地址隨機化開關詳情見下面鏈接:
http://visualgdb.com/gdbreference/commands/set_disable-randomization
當然上述告警其實也可以不通過gdb設置來完成,可以通過下面介紹的Docker參數可以達成。

seccomp

Docker默認情況下爲每個容器都設置了一個默認的seccom profile。一般情況下無需修改。但是docker依然支持
docker create或者docker run時候通過--security-opt seccomp=xxx參數來設置docker容器的seccomp策略。
xxx可以是一個json格式文件,裏面定義了docker容器每個具體的seccomp規則。也可以是字符unconfined表示關閉默認的docker seccomp 規則。
可以通過下面命令徹底關閉docker默認seccomp引入的任何限制
docker run -it --security-opt seccomp=unconfined centos:lastes
在運行上述gdb 調試命令run一個進程,告警信息終於徹底消失了。
Docker設置的seccomp 默認profile規則可以通過如下鏈接查詢到:
https://docs.docker.com/engine/security/seccomp/
本文就不再做詳細展開了。

Docker背後的實現技術

Docker實現seccomp控制

從Linux 2.6.23開始支持這種特性對進程能夠使用的系統調用進行控制,如此可以進行一些安全性策略。sandbox就是依賴於此技術。docker梳理了Linux的系統調用,從300+個系統調用中屏蔽掉了44個系統調用,但是又最大程度的不影響正常的應用使用系統。

  • 在Docker engine上有一個叫seccomp模塊提供了爲docker提供默認seccomp規則(GetDefaultProfile()函數),而我們在命令行配置的seccomp屬性會覆蓋默認的seccomp規則(在setSeccomp()函數裏)。最終規則在engine內部創建runc spec時刻函數createSpec生產。將seccomp傳遞到oci runtime spec的spec.linux.seccomp字段裏。
  • 而runc通過seccomp庫的InitSeccomp()函數,在Init一個容器時刻和exec觸發的將一個進程setns到容器內部時刻調用此函數 將seccomp屬性設置到容器的init進程或者exec進程裏。
  • Linux進程屬性裏有seccomp屬性,此屬性也會父子繼承,通過/proc/$pid/status裏可以看到進程的seccomp屬性。

Docker實現capabilities

從Linux 2.1開始支持的特性,將超級用戶的權限劃分爲多個組,每個進程都有一個capabilities屬性,子進程從自己的父進程中基礎capacities。這個特性和sudo不一樣,因爲sudo控制粒度太粗;而capabilities控制粒度很精細。linux有一系列的調用可以設置、查看,清除和比較進程的capabilities。可以通過:
man cap_set_flag
來查看這一系列的系統調用。而具體進程的capacities可以通過/proc/$pid/status中:
Capxxx字段看到,本文就不再展開。感興趣的朋友可以參考
https://www.cnblogs.com/iamfy/archive/2012/09/20/2694977.html

  • 在oci runtime spec裏規定了spec.Process.Capabilities屬性。runc中finializeNamespace()根據此屬性對進程的capabilities進行設置。此函數會在init容器和exec加一個進程到容器時刻調用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章