爲什麼你需要更小巧的容器?
這篇文章出人意料地登上了黑客新聞的頭版,並在那裏引發了一場富有成效的討論。我將試着總結一下主要的要點:
-
漏洞掃描器往往有很多誤報
-
一些報告的發現已經可以在上游和後端修復
-
有些可能完全不相關,因爲它們特定於某些深奧的架構
-
在鏡像倉庫(例如 Docker Hub)中,官方基礎鏡像從不(或很少)更新
-
隨着容器使用的增加,爲操作系統打補丁的負擔實際上從管理員和操作人員轉移到了開發人員身上
-
但並不是每個開發者都意識到這一點
-
有些人建議在每個 Dockerfile 的開頭添加 RUN apt-get update && apt-get -y upgrade,我嘗試了一下,在完全成熟的 Debian 10 發行版中,它提供了非常小的效果
-
但其他人反駁說,這會導致不可複製的構建,以及由於反向端口改變依賴的默認行爲而導致的潛在風險
-
這導致了一個公平的控制源存儲庫的建議
-
當然,這會讓事情變得更復雜
-
這就是爲什麼最典型的解決方案似乎是簡單的,忽略了這個問題
-
儘管掃描結果很好,但 Alpine 鏡像並不總是很好
-
因爲據報道 musl libc 比 glibc 慢,並不是每個依賴庫都爲這個平臺提供構建
以下是原文。
我最近在破解容器時注意到,Docker 開始在構建輸出中突出 Docker 掃描命令。我已經忽略它的存在有一段時間了,所以是時候嘗試一下了。
掃描官方 Python 鏡像
Docker 掃描命令使用一個第三方工具,稱爲 Snyk Container。顯然,這是某種漏洞掃描器。所以,我決定,主要是爲了好玩,掃描我的一個鏡像。這是一件非常基本的事情:
# 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"]
我運行 docker build -t python-flask,然後容器掃描 python-flask。令我非常驚訝的是,產生大量的輸出!下面是一段摘錄:
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/default-libmysqlclient-dev@1.0.5
From: mysql-defaults/default-libmysqlclient-dev@1.0.5 > mariadb-10.3/libmariadb-dev-compat@1:10.3.27-0+deb10u1 > mariadb-10.3/libmariadb-dev@1:10.3.27-0+deb10u1 > gnutls28/libgnutls28-dev@3.6.7-4+deb10u6 > gnutls28/libgnutls-dane0@3.6.7-4+deb10u6 > unbound/libunbound8@1.9.0-2+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/libtiff5@4.1.0+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/libtiff-dev@4.1.0+git191117-2~deb10u2 > tiff/libtiff5@4.1.0+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/libtiff-dev@4.1.0+git191117-2~deb10u2 > tiff/libtiffxx5@4.1.0+git191117-2~deb10u2 > tiff/libtiff5@4.1.0+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, libtool@2.4.6-9, imagemagick@8:6.9.10.23+dfsg-2.1+deb10u1, meta-common-packages@meta
From: gcc-defaults/g++@4:8.3.0-1 > gcc-8@8.3.0-6
From: libtool@2.4.6-9 > gcc-8@8.3.0-6
From: gcc-defaults/g++@4:8.3.0-1 > gcc-8/g++-8@8.3.0-6 > gcc-8@8.3.0-6
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/libdjvulibre-dev@3.5.27.1-10 > djvulibre/libdjvulibre21@3.5.27.1-10
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/libdjvulibre21@3.5.27.1-10
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/libdjvulibre-dev@3.5.27.1-10
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/libbluetooth-dev@5.50-1.2~deb10u1
From: bluez/libbluetooth-dev@5.50-1.2~deb10u1 > bluez/libbluetooth3@5.50-1.2~deb10u1
From: bluez/libbluetooth-dev@5.50-1.2~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
共發現漏洞 358 個,其中高級別 54 個,中級別 48 個。
仔細查看掃描報告後,我發現的大部分漏洞可能與 Debian 有關(參見信息:https://snyk.io/vuln/SNYK-DEBIAN10-… 稍高一點,報告中很多項目都有)。
顯然,python:3.9 鏡像是基於成熟的 Debian 10 發行版。
114MB 的潛在安全漏洞
老實說,這是令人興奮的!我知道容器越厚,潛在的攻擊面就越高。但無論如何,我沒想到會有這麼大的規模。
好的,讓我們試着把鏡像瘦身……
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 的鏡像給了我更好的結果:
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.
共發現 69 個漏洞,其中高級別 14 個,中級別 8 個。
基礎鏡像每兆字節有一個漏洞……
我們能做得更好嗎?讓我們嘗試 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.
5.6 MB Alpine!
掃描無發佈的 Python 鏡像
另一個可能解決膨脹容器問題的方案是谷歌所謂的 “非發行版”Docker 鏡像。項目描述說它是 “語言聚焦 Docker 鏡像,減去操作系統”。儘管這在 Python 的情況下很難實現,因爲它的標準庫依賴於一些高級操作系統功能。
我花了一段時間纔想出一個可以工作的非發行版 Python 鏡像。大多數示例都展示瞭如何使用一些簡單的腳本。然而,安裝 Flask(或任何其他依賴項)比我預期的要困難得多。由於 gcr,Python 的無發行版容器需要多階段的構建過程。gcr.io/distroless/python3 鏡像既沒有 PIP,也沒有 easy_install。首先,我試圖利用構建鏡像中的虛擬環境,然後將其複製到運行時鏡像中,並修改 PATH 變量。但它並沒有很好地工作,因爲基本的非發行版鏡像在文件系統佈局上做了一些自以爲是的佈局:
預料之中的是,Linux distr 仍然存在,但是它比 debian 還要薄
所以,我最終得到了以下 Dockerfile,允許我在非發行版的 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 掃描輸出以下結果:
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 個漏洞:6 個嚴重程度高,8 箇中等。聽起來好像比原始的 python:3.9 鏡像減少了 90%!它甚至比 python:3.9-slim 中的漏洞更少。
因此,我們的基於 Alpine 的 Python 鏡像是一個贏家!
掃描 Go 鏡像
我喜歡完全從零開始構建容器鏡像的想法,避免將任何 distr 內容放入其中。但爲此,你需要一種對靜態構建有深刻支持的語言,例如 Go:
FROM scratch
COPY hello /
CMD ["/hello"]
得出以下結果…… 沒錯,這就是它!
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 並不意味着沒有漏洞。但它確實降低了攻擊的可能性。
相反的結論
根據我的經驗,膨脹的鏡像通常是使用默認的 From bloated_base,或者因爲人們爲了將來的調試 / 故障排除故意將額外的工具放入鏡像的結果。
第一種可能是 Docker 過去大規模營銷活動的結果。爲了普及容器,你需要給人們一些看起來很酷和方便的東西。而且能夠在一秒鐘內從你的開發機器上啓動一個成熟的 Linux distr(某種程度上),即使在今天看起來也很酷。一些本地的修補和 / 或實驗從 docker run -it ubuntu 中獲益良多。然而,我希望它從來沒有被用於生產。但是從一個成熟的 Debian,CentOS,或者 Ubuntu 開始,Dockerfile 的例子實在是太多了,至少要避免將其中一些滲透到我們的生產環境中。
第二個原因看起來更合理。但我有一種感覺,這只是第一眼看到的感覺。理想情況下,應該有另一種方法來調試你的容器化服務。這種方法不需要將所有潛在需要的工具打包到一個容器中。最近添加的 kubectl 調試功能很好地證明了這一假設。它允許將臨時容器注入運行中的 Pod 中,這樣的容器可以擁有調試所需的所有東西。
更小的容器不僅僅意味着更快的構建和更小的磁盤和網絡利用率,它們還意味着更安全。
原文鏈接:https://iximiuz.com/en/posts/thick-container-vulnerabilities/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/3qfuygLophrpKSm4VuEj0g