摘要:在年月底時,我寫了一篇文章發布之際。為何有存在前面已經基本介紹了相關背景,并且也基本明確了就是在正式發布之前的最后一個版本,那為什么會出現呢我們首先要介紹今年的一個提權漏洞。
在 18 年 11 月底時,我寫了一篇文章 《runc 1.0-rc6 發布之際》 。如果你還不了解 runc 是什么,以及如何使用它,請參考我那篇文章。本文中,不再對其概念和用法等進行說明。
在 runc 1.0-rc6 發布之時,給版本的別名為 "For Real This Time",當時我們原定計劃是發布 1.0 的,但是作為基礎依賴軟件,我們認為當時的版本還有幾個問題:
不夠規范;
發布周期不明確;
為了給相關的 runtime 足夠的時間進行修正/升級,以及規范版本生命周期等,最終決定了發布 runc 1.0-rc6。
為何有 runc 1.0-rc7 存在前面已經基本介紹了相關背景,并且也基本明確了 rc6 就是在 1.0 正式發布之前的最后一個版本,那 rc7 為什么會出現呢?
CVE-2019-5736我們首先要介紹今年 runc 的一個提權漏洞 CVE-2019-5736 。
2019 年 2 月 11 日在 oss-security 郵件組正式批露該漏洞,攻擊者可以利用惡意容器覆蓋主機上的 runc 文件,從而達到攻擊的目的;(具體的攻擊方式此處略過),注意不要輕易使用來源不可信的鏡像創建容器便可有效避免被攻擊的可能。
簡單補充下可能被攻擊的方式:
運行惡意的 Docker 鏡像
在主機上執行 docker exec 進入容器內
關于容器安全或者容器的運行機制,其實涉及的點很多,我在去年的一次線上分享 《基于 GitLab 的 CI 實踐》 有提到過 Linux Security Modules(LSM)等相關的內容,對容器安全感興趣的朋友可以對 LSM 多了解下。
不過本文主要看的是 runc 如何修復該漏洞的,以及后續產生的影響。
修復方式// 對 memfd_create 系統調用做了個封裝 省略部分代碼 #if !defined(SYS_memfd_create) && defined(__NR_memfd_create) # define SYS_memfd_create __NR_memfd_create #endif #ifdef SYS_memfd_create # define HAVE_MEMFD_CREATE # ifndef MFD_CLOEXEC # define MFD_CLOEXEC 0x0001U # define MFD_ALLOW_SEALING 0x0002U # endif int memfd_create(const char *name, unsigned int flags) { return syscall(SYS_memfd_create, name, flags); } // 一個簡單的只讀緩存區 static char *read_file(char *path, size_t *length) { int fd; char buf[4096], *copy = NULL; if (!length) return NULL; fd = open(path, O_RDONLY | O_CLOEXEC); if (fd < 0) return NULL; *length = 0; for (;;) { int n; n = read(fd, buf, sizeof(buf)); if (n < 0) goto error; if (!n) break; copy = must_realloc(copy, (*length + n) * sizeof(*copy)); memcpy(copy + *length, buf, n); *length += n; } close(fd); return copy; error: close(fd); free(copy); return NULL; } // 將復制后的 fd 重賦值/執行 static int clone_binary(void) { int binfd, memfd; ssize_t sent = 0; #ifdef HAVE_MEMFD_CREATE memfd = memfd_create(RUNC_MEMFD_COMMENT, MFD_CLOEXEC | MFD_ALLOW_SEALING); #else memfd = open("/tmp", O_TMPFILE | O_EXCL | O_RDWR | O_CLOEXEC, 0711); #endif if (memfd < 0) return -ENOTRECOVERABLE; binfd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC); if (binfd < 0) goto error; sent = sendfile(memfd, binfd, NULL, RUNC_SENDFILE_MAX); close(binfd); if (sent < 0) goto error; #ifdef HAVE_MEMFD_CREATE int err = fcntl(memfd, F_ADD_SEALS, RUNC_MEMFD_SEALS); if (err < 0) goto error; #else int newfd; char *fdpath = NULL; if (asprintf(&fdpath, "/proc/self/fd/%d", memfd) < 0) goto error; newfd = open(fdpath, O_RDONLY | O_CLOEXEC); free(fdpath); if (newfd < 0) goto error; close(memfd); memfd = newfd; #endif return memfd; error: close(memfd); return -EIO; } int ensure_cloned_binary(void) { int execfd; char **argv = NULL, **envp = NULL; int cloned = is_self_cloned(); if (cloned > 0 || cloned == -ENOTRECOVERABLE) return cloned; if (fetchve(&argv, &envp) < 0) return -EINVAL; execfd = clone_binary(); if (execfd < 0) return -EIO; fexecve(execfd, argv, envp); return -ENOEXEC; }
省略掉了部分代碼,完整代碼可直接參考 runc 代碼倉庫 。
整個的修復邏輯我在上面的代碼中加了備注,總結來講其實就是:
創建了一個只存在于內存中的 memfd ;
將原本的 runc 拷貝至這個 memfd ;
在進入 namespace 前,通過這個 memfd 重新執行 runc ; (這是為了確保之后即使被攻擊/替換也操作的還是內存中的這個只讀的 runc)
經過以上的操作,就基本修復了 CVE-2019-5736 。
影響 內核相關在上面講完修復方式后,我們來看下會產生哪些影響。
涉及到了系統調用 memfd_create(2) 和 fcntl(2)
增加了系統調用,那自然就要看內核是否支持了。實際上,這些函數是在 2015 年 2 月(距這次修復整整 4 年,也挺有趣)被加入到 Linux 3.17 內核中的。
換句話說就是 凡是在此內核版本之前的系統,均無法正常使用該功能,對我們的影響就是,如果你在此版本內核之前的機器上使用了包含上述修復代碼的 runc 或構建在其之上的 containerd、 Docker 等都無法正常工作 。
以 Docker 舉例:安裝 docker-ce-18.09.2 或 docker-ce-18.06.3 可避免受 CVE-2019-5736 影響,但如果內核版本較低,在運行容器時可能會有如下情況出現: (不同版本/內核可能出現其他情況)
[tao@moelove ~]# docker run --rm my-registry/os/debian echo Hello docker: Error response from daemon: OCI runtime create failed: container_linux.go:344: starting container process caused "process_linux.go:293: copying bootstrap data to pipe caused "write init-p: broken pipe"": unknown.
解決辦法
升級內核;這是最直接的辦法,而且使用一個新版本的內核也能省去很多不必要的麻煩:)
rancher 提供了一個 runc-cve 的 patch,可兼容部分 3.x 內核的系統(我沒有測試過)
如果你不升級 runc/containerd/Docker 等版本的話,那建議你 1. 將 runc 可執行程序放到只讀文件系統上,可避免被覆蓋;2. 啟動容器時,啟用 SELinux; 3. 在容器內使用低權限用戶或者采用映射的方式,但保證用戶對主機上的 runc 程序無寫權限。
注意:
memfd_create 等相關系統調用,也被加入到了 Debian 3.16 和 Ubuntu 14.04 updates 中,當然也被反向移植到了 CentOS 7.3 內核 3.10.0-514 版本之后。 (Red Hat 給 CentOS 7.x 的 3.10 內核上反向移植了很多特性)
內存相關從上面的說明中,也很容易可以看到, 內存的使用上會有所增加,不過之后已做了修復。這里不再進行展開。
其他偶爾可能觸發一些內核 bug 之類的(總之建議升級 :)
等待 rc8 發布上面已經介紹了 1.0-rc7 出現的主要原因 CVE-2019-5736;當然這個版本中也有一些新特性和一些 bugfix 不過不是本文的主要內容,不再贅述。
值得一提的是這次的版本命名:runc 1.0-rc7 -- "The Eleventh Hour" 后面這個別名其實來自于一部英劇,感興趣也可以去看看。
至于下個版本是不是會是 1.0 正式版呢?目前來看應該不是,有極大可能會發布 runc 1.0-rc8 做一些 bugfix,讓我們拭目以待。
可以通過下面二維碼訂閱我的文章公眾號【MoeLove】
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/32970.html
摘要:在年月底時,我寫了一篇文章發布之際。為何有存在前面已經基本介紹了相關背景,并且也基本明確了就是在正式發布之前的最后一個版本,那為什么會出現呢我們首先要介紹今年的一個提權漏洞。 在 18 年 11 月底時,我寫了一篇文章 《runc 1.0-rc6 發布之際》 。如果你還不了解 runc 是什么,以及如何使用它,請參考我那篇文章。本文中,不再對其概念和用法等進行說明。 在 runc 1....
摘要:生態周報內容主要包含我所接觸到的生態相關的每周值得推薦的一些信息。在發現異常后官方團隊迅速采取行動并保護網站免受攻擊。期待能早日解決相關問題,并迎來的正式發布。這些功能適用于,,,,,,,和編寫的應用程序等,并將在下周放出技術預覽版本。 「K8S 生態周報」內容主要包含我所接觸到的 K8S 生態相關的每周值得推薦的一些信息。歡迎訂閱知乎專欄「k8s生態」。 Docker Hub 用戶隱...
摘要:生態周報內容主要包含我所接觸到的生態相關的每周值得推薦的一些信息。在發現異常后官方團隊迅速采取行動并保護網站免受攻擊。期待能早日解決相關問題,并迎來的正式發布。這些功能適用于,,,,,,,和編寫的應用程序等,并將在下周放出技術預覽版本。 「K8S 生態周報」內容主要包含我所接觸到的 K8S 生態相關的每周值得推薦的一些信息。歡迎訂閱知乎專欄「k8s生態」。 Docker Hub 用戶隱...
摘要:生態周報內容主要包含我所接觸到的生態相關的每周值得推薦的一些信息。歡迎訂閱知乎專欄生態。正式發布是一個用于本地搭建環境的工具,使用方法可參考使用搭建本地環境。其他特性請閱讀正式發布是一個使用來為構建的工具,現在是的項目。 「K8S 生態周報」內容主要包含我所接觸到的 K8S 生態相關的每周值得推薦的一些信息。歡迎訂閱知乎專欄「k8s生態」。 Kubernetes 1.14 正式發布 1...
摘要:生態周報內容主要包含我所接觸到的生態相關的每周值得推薦的一些信息。歡迎訂閱知乎專欄生態。正式發布是一個用于本地搭建環境的工具,使用方法可參考使用搭建本地環境。其他特性請閱讀正式發布是一個使用來為構建的工具,現在是的項目。 「K8S 生態周報」內容主要包含我所接觸到的 K8S 生態相關的每周值得推薦的一些信息。歡迎訂閱知乎專欄「k8s生態」。 Kubernetes 1.14 正式發布 1...
閱讀 1105·2021-11-16 11:45
閱讀 3124·2021-10-13 09:40
閱讀 714·2019-08-26 13:45
閱讀 1188·2019-08-26 13:32
閱讀 2167·2019-08-26 13:23
閱讀 911·2019-08-26 12:16
閱讀 2823·2019-08-26 11:37
閱讀 1748·2019-08-26 10:32