为什么在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*
例如:
但是這并不是問題的全部,對于上述測試程序,如果執行下面命令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加一個進程到容器時刻調用。
總結
以上是生活随笔為你收集整理的为什么在Docker里使用gdb调试器会报错的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Cloud 搭建 Hyst
- 下一篇: 微软发布Azure Cosmos DB产