為什么你需要更小巧的容器?


這篇文章出人意料地登上了黑客新聞的頭版,并在那里引發(fā)了一場富有成效的討論。我將試著總結(jié)一下主要的要點(diǎn):
漏洞掃描器往往有很多誤報(bào)
一些報(bào)告的發(fā)現(xiàn)已經(jīng)可以在上游和后端修復(fù)
有些可能完全不相關(guān),因?yàn)樗鼈兲囟ㄓ谀承┥願(yuàn)W的架構(gòu)
在鏡像倉庫(例如Docker Hub)中,官方基礎(chǔ)鏡像從不(或很少)更新
隨著容器使用的增加,為操作系統(tǒng)打補(bǔ)丁的負(fù)擔(dān)實(shí)際上從管理員和操作人員轉(zhuǎn)移到了開發(fā)人員身上
但并不是每個(gè)開發(fā)者都意識(shí)到這一點(diǎn)
有些人建議在每個(gè)Dockerfile的開頭添加RUN apt-get update && apt-get -y upgrade,我嘗試了一下,在完全成熟的Debian 10發(fā)行版中,它提供了非常小的效果
但其他人反駁說,這會(huì)導(dǎo)致不可復(fù)制的構(gòu)建,以及由于反向端口改變依賴的默認(rèn)行為而導(dǎo)致的潛在風(fēng)險(xiǎn)
這導(dǎo)致了一個(gè)公平的控制源存儲(chǔ)庫的建議
當(dāng)然,這會(huì)讓事情變得更復(fù)雜
這就是為什么最典型的解決方案似乎是簡單地,忽略了這個(gè)問題
盡管掃描結(jié)果很好,但Alpine鏡像并不總是很好
因?yàn)閾?jù)報(bào)道m(xù)usl libc比glibc慢,并不是每個(gè)依賴庫都為這個(gè)平臺(tái)提供構(gòu)建
以下是原文。
我最近在破解容器時(shí)注意到,Docker開始在構(gòu)建輸出中突出Docker掃描命令。我已經(jīng)忽略它的存在有一段時(shí)間了,所以是時(shí)候嘗試一下了。
掃描官方Python鏡像
Docker掃描命令使用一個(gè)第三方工具,稱為Snyk Container。顯然,這是某種漏洞掃描器。所以,我決定,主要是為了好玩,掃描我的一個(gè)鏡像。這是一件非?;镜氖虑椋?/p>
#?latest?stable?at?the?time??
FROM?python:3.9??
??
RUN?pip?install?Flask??
??
COPY?server.py?server.py??
??
ENV?FLASK_APP=server.py??
ENV?FLASK_RUN_PORT=5000??
ENV?FLASK_RUN_HOST=0.0.0.0??
??
EXPOSE?5000??
??
CMD?["flask",?"run"]
我運(yùn)行docker build -t python-flask,然后容器掃描python-flask。令我非常驚訝的是,產(chǎn)生大量的輸出!下面是一段摘錄:
Testing?python-flask...??
??
??Low?severity?vulnerability?found?in?unbound/libunbound8??
??Description:?Improper?Input?Validation??
??Info:?https://snyk.io/vuln/SNYK-DEBIAN10-UNBOUND-534899??
??Introduced?through:?mysql-defaults/[email protected]??
??From:?mysql-defaults/[email protected]?>?mariadb-10.3/libmariadb-dev-compat@1:10.3.27-0+deb10u1?>?mariadb-10.3/libmariadb-dev@1:10.3.27-0+deb10u1?>?gnutls28/[email protected]+deb10u6?>?gnutls28/[email protected]+deb10u6?>?unbound/[email protected]+deb10u2??
??
??Low?severity?vulnerability?found?in?tiff/libtiff5??
??Description:?Out-of-Bounds??
??Info:?https://snyk.io/vuln/SNYK-DEBIAN10-TIFF-1079067??
??Introduced?through:?imagemagick@8:6.9.10.23+dfsg-2.1+deb10u1,?imagemagick/libmagickcore-dev@8:6.9.10.23+dfsg-2.1+deb10u1??
??From:?imagemagick@8:6.9.10.23+dfsg-2.1+deb10u1?>?imagemagick/imagemagick-6.q16@8:6.9.10.23+dfsg-2.1+deb10u1?>?imagemagick/libmagickcore-6.q16-6@8:6.9.10.23+dfsg-2.1+deb10u1?>?tiff/[email protected]+git191117-2~deb10u2??
??From:?imagemagick/libmagickcore-dev@8:6.9.10.23+dfsg-2.1+deb10u1?>?imagemagick/libmagickcore-6.q16-dev@8:6.9.10.23+dfsg-2.1+deb10u1?>?tiff/[email protected]+git191117-2~deb10u2?>?tiff/[email protected]+git191117-2~deb10u2??
??From:?imagemagick/libmagickcore-dev@8:6.9.10.23+dfsg-2.1+deb10u1?>?imagemagick/libmagickcore-6.q16-dev@8:6.9.10.23+dfsg-2.1+deb10u1?>?tiff/[email protected]+git191117-2~deb10u2?>?tiff/[email protected]+git191117-2~deb10u2?>?tiff/[email protected]+git191117-2~deb10u2??
??and?3?more...??
??
...??
??
??High?severity?vulnerability?found?in?gcc-8??
??Description:?Insufficient?Entropy??
??Info:?https://snyk.io/vuln/SNYK-DEBIAN10-GCC8-469413??
??Introduced?through:?gcc-defaults/g++@4:8.3.0-1,[email protected],?imagemagick@8:6.9.10.23+dfsg-2.1+deb10u1,?meta-common-packages@meta??
??From:?gcc-defaults/g++@4:8.3.0-1?>[email protected]??
??From:[email protected]?>[email protected]??
??From:?gcc-defaults/g++@4:8.3.0-1?>?gcc-8/[email protected]?>[email protected]??
??and?23?more...??
??
??High?severity?vulnerability?found?in?djvulibre/libdjvulibre21??
??Description:?NULL?Pointer?Dereference??
??Info:?https://snyk.io/vuln/SNYK-DEBIAN10-DJVULIBRE-481572??
??Introduced?through:?imagemagick/libmagickcore-dev@8:6.9.10.23+dfsg-2.1+deb10u1??
??From:?imagemagick/libmagickcore-dev@8:6.9.10.23+dfsg-2.1+deb10u1?>?imagemagick/libmagickcore-6.q16-dev@8:6.9.10.23+dfsg-2.1+deb10u1?>?djvulibre/[email protected]?>?djvulibre/[email protected]??
??From:?imagemagick/libmagickcore-dev@8:6.9.10.23+dfsg-2.1+deb10u1?>?imagemagick/libmagickcore-6.q16-dev@8:6.9.10.23+dfsg-2.1+deb10u1?>?imagemagick/libmagickcore-6.q16-6-extra@8:6.9.10.23+dfsg-2.1+deb10u1?>?djvulibre/[email protected]??
??From:?imagemagick/libmagickcore-dev@8:6.9.10.23+dfsg-2.1+deb10u1?>?imagemagick/libmagickcore-6.q16-dev@8:6.9.10.23+dfsg-2.1+deb10u1?>?djvulibre/[email protected]??
??and?1?more...??
??
??High?severity?vulnerability?found?in?bluez/libbluetooth3??
??Description:?Double?Free??
??Info:?https://snyk.io/vuln/SNYK-DEBIAN10-BLUEZ-1018718??
??Introduced?through:?bluez/[email protected]~deb10u1??
??From:?bluez/[email protected]~deb10u1?>?bluez/[email protected]~deb10u1??
??From:?bluez/[email protected]~deb10u1??
??
??
??
Package?manager:???deb??
Project?name:??????docker-image|python-flask??
Docker?image:??????python-flask??
Platform:??????????linux/amd64??
??
Tested?431?dependencies?for?known?vulnerabilities,?found?358?vulnerabilities.??
??
For?more?free?scans?that?keep?your?images?secure,?sign?up?to?Snyk?at?https://dockr.ly/3ePqVcp
共發(fā)現(xiàn)漏洞358個(gè),其中高級(jí)別54個(gè),中級(jí)別48個(gè)。
仔細(xì)查看掃描報(bào)告后,我發(fā)現(xiàn)的大部分漏洞可能與Debian有關(guān)(參見信息:https://snyk.io/vuln/SNYK-DEBIAN10-…稍高一點(diǎn),報(bào)告中很多項(xiàng)目都有)。
顯然,python:3.9鏡像是基于成熟的Debian 10發(fā)行版。

老實(shí)說,這是令人興奮的!我知道容器越厚,潛在的攻擊面就越高。但無論如何,我沒想到會(huì)有這么大的規(guī)模。
好的,讓我們試著把鏡像瘦身……
FROM?python:3.9-slim??
??
RUN?pip?install?Flask??
??
COPY?server.py?server.py??
??
ENV?FLASK_APP=server.py??
ENV?FLASK\_RUN\_PORT=5000??
ENV?FLASK\_RUN\_HOST=0.0.0.0??
??
EXPOSE?5000??
??
CMD?["flask",?"run"]
掃描python:3.9-slim的鏡像給了我更好的結(jié)果:
Package?manager:???deb??
Project?name:??????docker-image|python-flask-slim??
Docker?image:??????python-flask-slim??
Platform:??????????linux/amd64??
??
Tested?94?dependencies?for?known?vulnerabilities,?found?69?vulnerabilities.
共發(fā)現(xiàn)69個(gè)漏洞,其中高級(jí)別14個(gè),中級(jí)別8個(gè)。

我們能做得更好嗎?讓我們嘗試python: 3.9-alpine:
FROM?python:3.9-alpine??
??
RUN?pip?install?Flask??
??
COPY?server.py?server.py??
??
ENV?FLASK_APP=server.py??
ENV?FLASK_RUN_PORT=5000??
ENV?FLASK_RUN_HOST=0.0.0.0??
??
EXPOSE?5000??
??
CMD?["flask",?"run"]
終于,0已知的漏洞!
Package?manager:???apk??
Project?name:??????docker-image|python-flask-alpine??
Docker?image:??????python-flask-alpine??
Platform:??????????linux/amd64??
??
??Tested?37?dependencies?for?known?issues,?no?vulnerable?paths?found.

掃描無發(fā)布的Python鏡像
另一個(gè)可能解決膨脹容器問題的方案是谷歌所謂的“非發(fā)行版”Docker鏡像。項(xiàng)目描述說它是“語言聚焦Docker鏡像,減去操作系統(tǒng)”。盡管這在Python的情況下很難實(shí)現(xiàn),因?yàn)樗臉?biāo)準(zhǔn)庫依賴于一些高級(jí)操作系統(tǒng)功能。
我花了一段時(shí)間才想出一個(gè)可以工作的非發(fā)行版Python鏡像。大多數(shù)示例都展示了如何使用一些簡單的腳本。然而,安裝Flask(或任何其他依賴項(xiàng))比我預(yù)期的要困難得多。由于gcr,Python的無發(fā)行版容器需要多階段的構(gòu)建過程。gcr.io/distroless/python3鏡像既沒有PIP,也沒有easy_install。首先,我試圖利用構(gòu)建鏡像中的虛擬環(huán)境,然后將其復(fù)制到運(yùn)行時(shí)鏡像中,并修改PATH變量。但它并沒有很好地工作,因?yàn)榛镜姆前l(fā)行版鏡像在文件系統(tǒng)布局上做了一些自以為是的布局:

所以,我最終得到了以下Dockerfile,允許我在非發(fā)行版的Python鏡像中安裝Flask:
#?Build?image??
FROM?python:3.7-slim?AS?build-env??
??
RUN?python?-m?pip?install?Flask??
??
#?Runtime?image??
FROM?gcr.io/distroless/python3??
??
COPY?--from=build-env?/usr/local/bin/flask?/usr/local/bin/flask??
COPY?--from=build-env?/usr/local/lib/python3.7/site-packages?/usr/local/lib/python3.7/site-packages??
??
WORKDIR?/app??
??
COPY?server.py?server.py??
??
#?Important?line!??
ENV?PYTHONPATH=/usr/local/lib/python3.7/site-packages??
??
ENV?FLASK_APP=server.py??
ENV?FLASK_RUN_PORT=5000??
ENV?FLASK_RUN_HOST=0.0.0.0??
??
EXPOSE?5000??
??
CMD?["/usr/local/bin/flask",?"run"]
掃描它與Docker掃描輸出以下結(jié)果:
Package?manager:???deb??
Project?name:??????docker-image|python-flask-distroless??
Docker?image:??????python-flask-distroless??
Platform:??????????linux/amd64??
??
Tested?25?dependencies?for?known?vulnerabilities,?found?37?vulnerabilities.
所以,只有37個(gè)漏洞:6個(gè)嚴(yán)重程度高,8個(gè)中等。聽起來好像比原始的python:3.9鏡像減少了90%!它甚至比python:3.9-slim中的漏洞更少。
因此,我們的基于Alpine的Python鏡像是一個(gè)贏家!
掃描Go鏡像
我喜歡完全從零開始構(gòu)建容器鏡像的想法,避免將任何distr內(nèi)容放入其中。但為此,你需要一種對(duì)靜態(tài)構(gòu)建有深刻支持的語言,例如Go:
FROM?scratch??
??
COPY?hello?/
CMD?["/hello"]
得出以下結(jié)果……沒錯(cuò),這就是它!
Testing?go-scratch...??
??
Package?manager:???linux??
Project?name:??????docker-image|go-scratch??
Docker?image:??????go-scratch??
Platform:??????????linux/amd64??
??
??Tested?go-scratch?for?known?vulnerabilities,?no?vulnerable?paths?found.
沒有distr并不意味著沒有漏洞。但它確實(shí)降低了攻擊的可能性。

相反的結(jié)論
根據(jù)我的經(jīng)驗(yàn),膨脹的鏡像通常是使用默認(rèn)的From bloated_base,或者因?yàn)槿藗優(yōu)榱藢淼恼{(diào)試/故障排除故意將額外的工具放入鏡像的結(jié)果。
第一種可能是Docker過去大規(guī)模營銷活動(dòng)的結(jié)果。為了普及容器,你需要給人們一些看起來很酷和方便的東西。而且能夠在一秒鐘內(nèi)從你的開發(fā)機(jī)器上啟動(dòng)一個(gè)成熟的Linux distr(某種程度上),即使在今天看起來也很酷。一些本地的修補(bǔ)和/或?qū)嶒?yàn)從docker run -it ubuntu中獲益良多。然而,我希望它從來沒有被用于生產(chǎn)。但是從一個(gè)成熟的Debian,CentOS,或者Ubuntu開始,Dockerfile的例子實(shí)在是太多了,至少要避免將其中一些滲透到我們的生產(chǎn)環(huán)境中。
第二個(gè)原因看起來更合理。但我有一種感覺,這只是第一眼看到的感覺。理想情況下,應(yīng)該有另一種方法來調(diào)試你的容器化服務(wù)。這種方法不需要將所有潛在需要的工具打包到一個(gè)容器中。最近添加的kubectl調(diào)試功能很好地證明了這一假設(shè)。它允許將臨時(shí)容器注入運(yùn)行中的Pod中,這樣的容器可以擁有調(diào)試所需的所有東西。
更小的容器不僅僅意味著更快的構(gòu)建和更小的磁盤和網(wǎng)絡(luò)利用率,它們還意味著更安全。
有收獲,點(diǎn)個(gè)在看?


