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

19266037c8018bab000b0847cd14755a.png

这篇文章出人意料地登上了黑客新闻的头版,并在那里引发了一场富有成效的讨论。我将试着总结一下主要的要点:

  • 漏洞扫描器往往有很多误报

    • 一些报告的发现已经可以在上游和后端修复

    • 有些可能完全不相关,因为它们特定于某些深奥的架构

  • 在镜像仓库(例如Docker Hub)中,官方基础镜像从不(或很少)更新

  • 随着容器使用的增加,为操作系统打补丁的负担实际上从管理员和操作人员转移到了开发人员身上

  • 但并不是每个开发者都意识到这一点

    • 有些人建议在每个Dockerfile的开头添加RUN apt-get update && apt-get -y upgrade,我尝试了一下,在完全成熟的Debian 10发行版中,它提供了非常小的效果

    • 但其他人反驳说,这会导致不可复制的构建,以及由于反向端口改变依赖的默认行为而导致的潜在风险

    • 这导致了一个公平的控制源存储库的建议

    • 当然,这会让事情变得更复杂

    • 这就是为什么最典型的解决方案似乎是简单地,忽略了这个问题

  • 尽管扫描结果很好,但Alpine镜像并不总是很好

    • 因为据报道musl libc比glibc慢,并不是每个依赖库都为这个平台提供构建

以下是原文。

我最近在破解容器时注意到,Docker开始在构建输出中突出Docker扫描命令。我已经忽略它的存在有一段时间了,所以是时候尝试一下了。

扫描官方Python镜像

e615268dc00224464c357860cc06302a.png

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发行版。

1108d0c1f2840bc2a568f508a2accd6e.png

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个。

f030b64dbc22b80261b52ce63cfa23dc.png

基础镜像每兆字节有一个漏洞……

我们能做得更好吗?让我们尝试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.

05eadd06b5afabb94bf49a4c70039b5c.png

5.6 MB Alpine!

扫描无发布的Python镜像

d334c8168b22a21f09d95d77c46689b9.png

另一个可能解决膨胀容器问题的方案是谷歌所谓的“非发行版”Docker镜像。项目描述说它是“语言聚焦Docker镜像,减去操作系统”。尽管这在Python的情况下很难实现,因为它的标准库依赖于一些高级操作系统功能。

我花了一段时间才想出一个可以工作的非发行版Python镜像。大多数示例都展示了如何使用一些简单的脚本。然而,安装Flask(或任何其他依赖项)比我预期的要困难得多。由于gcr,Python的无发行版容器需要多阶段的构建过程。gcr.io/distroless/python3镜像既没有PIP,也没有easy_install。首先,我试图利用构建镜像中的虚拟环境,然后将其复制到运行时镜像中,并修改PATH变量。但它并没有很好地工作,因为基本的非发行版镜像在文件系统布局上做了一些自以为是的布局:

1b8d096fb4ab3078c927ef68f084604d.png

预料之中的是,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镜像

3f562826ee6b79b808c0b69e6824e2c9.png

我喜欢完全从零开始构建容器镜像的想法,避免将任何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并不意味着没有漏洞。但它确实降低了攻击的可能性。

4f021451299fff49c4f67624b7cb2a35.png

相反的结论

e3dd6eae8dc888f3c3afbaa6058ed033.png

根据我的经验,膨胀的镜像通常是使用默认的From bloated_base,或者因为人们为了将来的调试/故障排除故意将额外的工具放入镜像的结果。

第一种可能是Docker过去大规模营销活动的结果。为了普及容器,你需要给人们一些看起来很酷和方便的东西。而且能够在一秒钟内从你的开发机器上启动一个成熟的Linux distr(某种程度上),即使在今天看起来也很酷。一些本地的修补和/或实验从docker run -it ubuntu中获益良多。然而,我希望它从来没有被用于生产。但是从一个成熟的Debian,CentOS,或者Ubuntu开始,Dockerfile的例子实在是太多了,至少要避免将其中一些渗透到我们的生产环境中。

第二个原因看起来更合理。但我有一种感觉,这只是第一眼看到的感觉。理想情况下,应该有另一种方法来调试你的容器化服务。这种方法不需要将所有潜在需要的工具打包到一个容器中。最近添加的kubectl调试功能很好地证明了这一假设。它允许将临时容器注入运行中的Pod中,这样的容器可以拥有调试所需的所有东西。

更小的容器不仅仅意味着更快的构建和更小的磁盘和网络利用率,它们还意味着更安全。

原文链接:https://iximiuz.com/en/posts/thick-container-vulnerabilities/

Kubernetes线下实战与CKA培训

aed99ceef445eb54ff306002e6fb4f44.png

本次培训在北京开班,基于最新考纲,理论结合实战,通过线下授课、考题解读、模拟演练等方式,帮助学员快速掌握Kubernetes的理论知识和专业技能,并针对考试做特别强化训练,让学员能从容面对CKA认证考试,使学员既能掌握Kubernetes相关知识,又能通过CKA认证考试,理论、实践、考证一网打尽,学员可多次参加培训,直到通过认证。点击下方图片或者阅读原文链接查看详情。

cbd77297f13e7d15c369f76063a72407.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值