Docker 的鏡像並不安全!
Docker所報告的,一個已下載的鏡像經過「驗證」,它基於的僅僅是一個標記清單(signed manifest),而Docker卻從未據此清單對鏡像的校驗和進行驗證。一名攻擊者以此可以提供任意所謂具有標記清單的鏡像。一系列嚴重漏洞的大門就此敞開。
鏡像經由HTTPS伺服器下載後,通過一個未加密的管道流進入Docker守護進程:
[decompress] -> [tarsum] -> [unpack]
這條管道的性能沒有問題,但是卻完全沒有經過加密。不可信的輸入在簽名驗證之前是不應當進入管道的。不幸的是,Docker在上面處理鏡像的三個步驟中,都沒有對校驗和進行驗證。
然而,不論Docker如何聲明,實際上鏡像的校驗和(Checksum)從未經過校驗。下面是Docker與鏡像校驗和的驗證相關的代碼片段,即使我提交了校驗和不匹配的鏡像,都無法觸發警告信息。
if img.Checksum != "" && img.Checksum != checksum {
log.Warnf("image layer checksum mismatch: computed %q,
expected %q", checksum, img.Checksum)
}
不安全的處理管道
解壓縮
Docker支持三種壓縮演算法:gzip、bzip2和xz。前兩種使用Go的標準庫實現,是內存安全(memory-safe)的,因此這裡我預計的攻擊類型應該是拒絕服務類的攻擊,包括CPU和內存使用上的當機或過載等等。
第三種壓縮演算法,xz,比較有意思。因為沒有現成的Go實現,Docker 通過執行(exec)xz二進位命令來實現解壓縮。
xz二進位程序來自於XZ Utils項目,由大概2萬行C代碼生成而來。而C語言不是一門內存安全的語言。這意味著C程序的惡意輸入,在這裡也就是Docker鏡像的XZ Utils解包程序,潛在地存在可能會執行任意代碼的風險。
Docker以root許可權運行 xz 命令,更加惡化了這一潛在威脅。這意味著如果在xz中出現了一個漏洞,對docker pull命令的調用就會導致用戶整個系統的完全淪陷。
Tarsum
對tarsum的使用,其出發點是好的,但卻是最大的敗筆。為了得到任意一個加密tar文件的準確校驗和,Docker先對tar文件進行解密,然後求出特定部分的哈希值,同時排除剩餘的部分,而這些步驟的順序都是固定的。
由於其生成校驗和的步驟固定,它解碼不可信數據的過程就有可能被設計成攻破tarsum的代碼。這裡潛在的攻擊既包括拒絕服務攻擊,還有邏輯上的漏洞攻擊,可能導致文件被感染、忽略、進程被篡改、植入等等,這一切攻擊的同時,校驗和可能都是不變的。
解包
解包的過程包括tar解碼和生成硬碟上的文件。這一過程尤其危險,因為在解包寫入硬碟的過程中有另外三個已報告的漏洞。
任何情形下未經驗證的數據都不應當解包後直接寫入硬碟。
libtrust
Docker的工具包libtrust,號稱「通過一個分散式的信任圖表進行認證和訪問控制」。很不幸,對此官方沒有任何具體的說明,看起來它好像是實現了一些javascript對象標記和加密規格以及其他一些未說明的演算法。
使用libtrust下載一個清單經過簽名和認證的鏡像,就可以觸發下面這條不準確的信息(說不準確,是因為事實上它驗證的只是清單,並非真正的鏡像):
ubuntu:14.04: The image you are pulling has been verified(您所拉取的鏡像已經經過驗證)
目前只有Docker公司「官方」發布的鏡像清單使用了這套簽名系統,但是上次我參加Docker管理諮詢委員會的會議討論時,我所理解的是,Docker公司正計劃在未來擴大部署這套系統。他們的目標是以Docker公司為中心,控制一個認證授權機構,對鏡像進行簽名和(或)客戶認證。
我試圖從Docker的代碼中找到簽名秘鑰,但是沒找到。好像它並不像我們所期望的把密鑰嵌在二進位代碼中,而是在每次鏡像下載前,由Docker守護進程通過HTTPS從CDN遠程獲取。這是一個多麼糟糕的方案,有無數種攻擊手段可能會將可信密鑰替換成惡意密鑰。這些攻擊包括但不限於:CDN供應商出問題、CDN初始密鑰出現問題、客戶端下載時的中間人攻擊等等。
補救
研究結束前,我報告了一些在tarsum系統中發現的問題,但是截至目前我報告的這些問題仍然沒有修復。
要改進Docker鏡像下載系統的安全問題,我認為應當有以下措施:
摒棄tarsum並且真正對鏡像本身進行驗證
出於安全原因tarsum應當被摒棄,同時,鏡像在完整下載後、其他步驟開始前,就對鏡像的加密簽名進行驗證。
添加許可權隔離
鏡像的處理過程中涉及到解壓縮或解包的步驟必須在隔離的進程(容器?)中進行,即只給予其操作所需的最小許可權。任何場景下都不應當使用root運行xz這樣的解壓縮工具。
替換 libtrust
應當用更新框架(The Update Framework)替換掉libtrust,這是專門設計用來解決軟體二進位簽名此類實際問題的。其威脅模型非常全方位,能夠解決libtrust中未曾考慮到的諸多問題,目前已經有了完整的說明文檔。除了已有的Python實現,我已經開始著手用Go語言實現的工作,也歡迎大家的貢獻。
作為將更新框架加入Docker的一部分,還應當加入一個本地密鑰存儲池,將root密鑰與registry的地址進行映射,這樣用戶就可以擁有他們自己的簽名密鑰,而不必使用Docker公司的了。
我注意到使用非Docker公司官方的第三方倉庫往往會是一種非常糟糕的用戶體驗。Docker也會將第三方的倉庫內容降為二等地位來看待,即使不因為技術上的原因。這個問題不僅僅是生態問題,還是一個終端用戶的安全問題。針對第三方倉庫的全方位、去中心化的安全模型既必須又迫切。我希望Docker公司在重新設計他們的安全模型和鏡像認證系統時能採納這一點。
結論
Docker用戶應當意識到負責下載鏡像的代碼是非常不安全的。用戶們應當只下載那些出處沒有問題的鏡像。目前,這裡的「沒有問題」並不包括Docker公司的「可信(trusted)」鏡像,例如官方的Ubuntu和其他基礎鏡像。
最好的選擇就是在本地屏蔽 index.docker.io,然後使用docker load命令在導入Docker之前手動下載鏡像並對其進行驗證。Red Hat的安全博客有一篇很好的文章,大家可以看看。
感謝Lewis Marshall指出tarsum從未真正驗證。
參考
- 校驗和的代碼
- cloc介紹了18141行沒有空格沒有注釋的C代碼,以及5900行的header代碼,版本號為v5.2.0。
- Android中也發現了類似的bug,能夠感染已簽名包中的任意文件。同樣出現問題的還有Windows的Authenticode認證系統,二進位文件會被篡改。
- 特別的:CVE-2014-6407、 CVE-2014-9356以及 CVE-2014-9357。目前已有兩個Docker安全發布有了回應。
- 參見2014-10-28 DGAB會議記錄的第8頁。
via: https://titanous.com/posts/docker-insecurity
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive