Distroless加固容器安全

news/2024/7/2 21:36:24
谷歌现在通过提供 Distroless 镜像向全世界开放这种能力。谷歌构建的这些镜像的目标是只包含你的应用程序及其依赖项,同时它们将没有常规 Linux 发行版的所有特性,包括 shell。

Distroless加固容器安全Distroless加固容器安全

使用Distroless镜像来保护Kubernetes上的容器。容器改变了我们看待技术基础设施的方式。这是我们运行应用程序方式的一次巨大飞跃。容器编排和云服务一起为我们提供了一种近乎无限规模的无缝扩展能力。根据定义,容器应该包含「应用程序」及其「运行时依赖项」。然而,在现实中,它们包含的远不止这些。标准容器基础映像包含标准Linux发行版中可以找到的包管理器、shell和其他程序。虽然这些都是构建容器镜像所必需的,但它们不应该成为最终镜像的一部分。例如,一旦你把包安装好了,就不再需要在容器中使用apt等包管理工具了。这不仅使你的容器里充满了不必要的软件包和程序,而且还为网络罪犯提供了攻击特定程序漏洞的机会。你应该始终了解容器运行时中存在什么,并且应该精确地限制其只包含应用程序所需的依赖项。除了那些必要的,你不应该安装任何东西。一些领先的科技巨头,如谷歌,有多年在生产中运行容器的经验,已经采用了这种方法。

谷歌现在通过提供Distroless镜像向全世界开放这种能力。谷歌构建的这些镜像的目标是只包含你的应用程序及其依赖项,同时它们将没有常规Linux发行版的所有特性,包括shell。「这意味着虽然可以像以前一样运行应用程序的容器,但不能在容器运行的时候进入容器内」。这是一个重大的安全改进,因为你现在已经为黑客通过shell进入你的容器关上了大门。

Distroless 基础镜像

谷歌为大多数流行的编程语言和平台提供了 Distroless 的基础镜像。

以下基础镜像是正式发布的版本:

  1. gcr.io/distroless/static-debian10
  1. gcr.io/distroless/base-debian10
  1. gcr.io/distroless/java-debian10
  1. gcr.io/distroless/cc-debian10
  1. gcr.io/distroless/nodejs-debian10

下面的基础镜像仍在实验阶段,不推荐用于生产环境:

  1. gcr.io/distroless/python2.7-debian10
  1. gcr.io/distroless/python3-debian10
  1. gcr.io/distroless/java/jetty-debian10
  1. gcr.io/distroless/dotnet

构建 Distroless 镜像

谷歌在内部使用 Bazel 来构建容器映像,但是我们可以使用 Docker 来做同样的事情。关于使用 Distroless 镜像的一个有争议的问题是:当我们有一个 Distroless 镜像时,我们如何使用 Dockerfile 来构建我们的应用程序呢?

通常,Dockerfile 以一个标准的 OS 基础镜像开始,然后是创建适当的运行时构建所需执行的多个步骤。这包括包的安装,为此需要像 apt 或 yum 这样的包管理器。

有两种方法:

  1. 先在 Docker 外部构建好你的应用程序,然后使用 Dockerfile 中的 ADD 或 COPY 指令将二进制包复制到容器中。
  1. 使用多阶段 Docker 构建。这是 Docker 17.05 及以后版本的一个新特性,它允许你将构建分为不同的阶段。第一阶段可以从标准的 OS 基础镜像开始,可以帮助你构建应用程序;第二阶段可以简单地从第一阶段获取构建的文件并使用 Distroless 作为基础镜像。

为了理解它是如何工作的,让我们使用多阶段构建流程进行一个实际操作练习。

必要条件

你需要具备以下内容:

  1. Docker 版本大于等于 17.05,用于构建镜像
  1. 可选的Kubernetes集群用于实践练习的第二部分。如果你想在 Docker 中运行你的容器,你可以使用等价的docker命令。

GitHub代码仓

作为实践练习,将 此代码仓 Fork 到你的 GitHub 帐号下,然后克隆 GitHub 代码仓并使用 cd 进入到项目目录下。

该代码仓包含一个 Python 的 Flask 应用程序,当你调用API时,该应用程序会响应 Hello World!。

app.py 文件如下所示:

from flask import Flask app = Flask(__name__) @app.route("/") 
def hello(): return "Hello World!" if __name__ == '__main__': app.run(host='0.0.0.0', debug=True) 

Dockerfile 包含两个阶段:

FROM python:2.7-slim AS build 
ADD . /app 
WORKDIR /app 
RUN pip install --upgrade pip 
RUN pip install -r ./requirements.txt FROM gcr.io/distroless/python2.7 
COPY --from=build /app /app 
COPY --from=build /usr/local/lib/python2.7/site-packages /usr/local/lib/python2.7/site-packages 
WORKDIR /app 
ENV PYTHONPATH=/usr/local/lib/python2.7/site-packages 
EXPOSE 5000 
CMD ["app.py"] 

构建阶段:

  1. 从python:2.7-slim的基础镜像开始
  1. 将应用程序复制到/app目录下
  1. 升级pip并安装依赖

Distroless 阶段:

  1. 从 gcr.io/distroless/python2.7 的基础镜像开始
  1. 将应用程序从构建阶段的 /app 目录复制到当前阶段的 /app 目录
  1. 将 python 的 site-packages 从构建阶段复制到当前阶段的 site-packages 目录
  1. 设置工作目录到 /app,将 python PATH 设置为 site-packages 目录,并暴露 5000 端口
  1. 使用 CMD 指令运行 app.py

由于 Disroless 镜像不包含 shell,所以应该在最后使用 CMD 指令。如果不这样做,Docker 将认为它是一个 shell CMD,并试图这样执行它,但这是不工作的。

构建镜像:

$ docker build -t /flask-hello-world-distroless . 
Sending build context to Docker daemon  95.74kB 
Step 1/12 : FROM python:2.7-slim AS build ---> eeb27ee6b893 
Step 2/12 : ADD . /app ---> a01dc81df193 
Step 3/12 : WORKDIR /app ---> Running in 48ccf6b990e4 
Removing intermediate container 48ccf6b990e4 ---> 2e5e335be678 
Step 4/12 : RUN pip install --upgrade pip ---> Running in 583be3d0b8cc 
Collecting pip Downloading pip-20.1.1-py2.py3-none-any.whl (1.5 MB) 
Installing collected packages: pip Attempting uninstall: pip Found existing installation: pip 20.0.2 Uninstalling pip-20.0.2: Successfully uninstalled pip-20.0.2 
Successfully installed pip-20.1.1 
Removing intermediate container 583be3d0b8cc 
................................... 
Successfully installed Jinja2-2.11.2 MarkupSafe-0.23 click-7.1.2 flask-1.1.2 itsdangerous-0.24 werkzeug-1.0.1 
Removing intermediate container c4d00b1abf4a ---> 01cbadcc531f 
Step 6/12 : FROM gcr.io/distroless/python2.7 ---> 796952c43cc4 
Step 7/12 : COPY --from=build /app /app ---> 92657682cdcc 
Step 8/12 : COPY --from=build /usr/local/lib/python2.7/site-packages /usr/local/lib/python2.7/site-packages ---> faafd06edeac 
Step 9/12 : WORKDIR /app ---> Running in 0cf545aa0e62 
Removing intermediate container 0cf545aa0e62 ---> 4c4af4333209 
Step 10/12 : ENV PYTHONPATH=/usr/local/lib/python2.7/site-packages ---> Running in 681ae3cd51cc 
Removing intermediate container 681ae3cd51cc ---> 564f48eff90a 
Step 11/12 : EXPOSE 5000 ---> Running in 7ff5c073d568 
Removing intermediate container 7ff5c073d568 ---> ccc3d211d295 
Step 12/12 : CMD ["app.py"] ---> Running in 2b2c2f111423 
Removing intermediate container 2b2c2f111423 ---> 76d13d2f61cd 
Successfully built 76d13d2f61cd 
Successfully tagged /flask-hello-world-distroless:latest 

登录到 DockerHub 并推送镜像:

docker login 
docker push /flask-hello-world-distroless:latest

登录到 DockerHub(或者你的私有镜像仓),你应该会看到容器镜像可以使用:

Distroless加固容器安全Distroless加固容器安全

 

如果你看一下压缩后的大小,它只有 23.36 MB。如果你使用 slim 发行版作为基础镜像,它将占用 56 MB。

Distroless加固容器安全Distroless加固容器安全

你已经减少了超过一半的容器占用空间。That’s amazing!

在 Kubernetes 中运行容器

为了测试构建是否有效,让我们在 Kubernetes 集群中运行容器。如果你没有 Kubernetes,你可以运行等价的 Docker 命令来做相同的活动,因为 Kubectl 和 Docker 命令是相似的。

我在代码仓中创建了一个 kubernetes.yaml 文件,该文件包含使用我们构建的镜像的 Deployment 和 负载均衡的 Service。

--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: name: flask-deployment 
spec: selector: matchLabels: app: flask replicas: 2 template: metadata: labels: app: flask spec: containers: - name: flask image: bharamicrosystems/flask-hello-world-distroless ports: - containerPort: 5000 
--- 
apiVersion: v1 
kind: Service 
metadata: name: flask-service 
spec: selector: app: flask ports: - port: 80 targetPort: 5000 type: LoadBalancer 

这是一个非常简单的设置。负载均衡器监听端口 80 并映射到目标端口 5000。这些 Pods 在默认的 5000 端口上监听 Flask 应用程序。

应用:

$ kubectl apply -f kubernetes.yaml 
deployment.apps/flask-deployment created 
service/flask-service created 

我们查看一下所有的资源,看看我们已经创建了什么:

$ kubectl get all 
NAME                                    READY   STATUS    RESTARTS   AGE 
pod/flask-deployment-576496558b-hnbxt   1/1     Running   0          47s 
pod/flask-deployment-576496558b-hszpq   1/1     Running   0          73s NAME                    TYPE           CLUSTER-IP   EXTERNAL-IP      PORT(S)        AGE 
service/flask-service   LoadBalancer   10.8.9.163   35.184.113.120   80:31357/TCP   86s 
service/kubernetes      ClusterIP      10.8.0.1                443/TCP        26m NAME                               READY   UP-TO-DATE   AVAILABLE   AGE 
deployment.apps/flask-deployment   2/2     2            2           88s NAME                                          DESIRED   CURRENT   READY   AGE 
replicaset.apps/flask-deployment-576496558b   2         2         2       89s 

我们看到存在两个 Pods、一个 Deployment、一个带有外部 IP 的 LoadBalancer 服务和一个 ReplicaSet。

让我们访问应用程序:

$ curl http://35.184.113.120 
Hello World! 

我们得到了 Hello World!。这表明 Flask 应用程序在正常工作。

使用 Shell 对应用程序进行访问

正如我在引言中所描述的,Disroless 容器中没有 shell,因此不可能进入到容器内。然而,让我们试着在容器中执行 exec:

$ kubectl exec -it flask-deployment-576496558b-hnbxt /bin/bash 
OCI runtime exec failed: exec failed: container_linux.go:349: starting container process caused "exec: \"/bin/bash\": stat /bin/bash: no such file or directory": unknown 
command terminated with exit code 126 

我们无法连接到容器上。

容器日志呢?如果拿不到容器日志,我们就失去了调试应用程序的方法。

让我们试着去拿日志:

$ kubectl logs flask-deployment-576496558b-hnbxt * Running on http://0.0.0.0:5000/ * Restarting with reloader 
10.128.0.4 - - [31/May/2020 13:40:27] "GET / HTTP/1.1" 200 - 
10.128.0.3 - - [31/May/2020 13:42:01] "GET / HTTP/1.1" 200 - 

所以容器日志是可以被获取到的!

结论

使用 Distroless 作为基础镜像是一种令人兴奋的保护容器安全的方式。由于镜像小并且仅包含应用程序和依赖项,因此它为应用程序提供了最小的攻击面。它在更大程度上提高了应用程序的安全性,所以它是保护容器安全的好方法。


http://lihuaxi.xjx100.cn/news/284312.html

相关文章

GAN实战——书法字体生成练习赛开始报名啦!

生成式对抗网络(GAN)是近年来大热的深度学习模型。目前GAN最常使用的场景就是图像生成,作为一种优秀的生成式模型,GAN引爆了许多图像生成的有趣应用。在图像生成模型的质量上,生成对抗网络技术可以说实现了飞跃&#x…

用C语言实现扫雷小游戏(附上思路+项目展示+源代码)

文章目录前言一、扫雷小游戏整体思路讲解。二、game.c各游戏功能函数的讲解1.InitBoard 初始化数组函数讲解2.DisplayBoard 打印格子函数讲解3.Setmine 电脑生成雷 函数讲解4.GetMineCount 格子表示周围雷的个数 函数讲解5.FindMine 排雷函数实现game.c源代码展示:三…

ubuntu samba 安装

Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件,是一种在局域网上共享文件和打印机的一种通信协议。 1. 安装 sudo apt-get install samba samba-common 2. 创建共享目录 sudo mkdir /samba/share -p 3. 修改共享目录权限 sudo chmod 777 /samba/share/ -R 4…

BERT拿下最佳长论文奖!NAACL 2019最佳论文奖公布

点击上方↑↑↑蓝字关注我们~「2019 Python开发者日」全日程揭晓,请扫码咨询 ↑↑↑作者 | 刘静编辑 | 李尔客本文经授权转自公众号图灵Topia(ID:turingtopia)今日,自然语言处理顶会NAACL 2019最佳论文奖公布&#xff…

5G消息三种类型

要知道从人类诞生起,交流与通讯就是我们最基本的沟通需求,通讯行业的诞生,更是大大丰富了人类的交流方式,随着 5G 时代的到来,这次重磅出击的 5G 消息,又是什么样的呢?5G 时代,三大运营商联合发…

【C语言】算数操作符 移位操作符 以及 sizeof单目操作符讲解

目录1.算术操作符2. 移位操作符2.1 左移操作符2.2 右移操作符3.sizeof 单目操作符介绍操作符介绍:C语言操作符分为: 算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符 逻辑操作符 条件操作符 逗号表达式 下标引用、函数调用和结构成员下…

凌晨!科比因坠机离世, 球迷泪崩:凌晨4点,我的青春没了

点击上方“视学算法”,选择“星标”在看|留言|真爱来源:21世纪经济报道综合央视新闻、新浪体育、钱江晚报、开发者技术前线出品等。编辑 可可北京时间1月27日凌晨消息,据媒体报道,湖人队传奇巨星科比-布莱恩特在一起直升机事故中不…

从数据中台到AI中台:一种敏捷的智能业务支持方案( 附视频讲解) | 技术头条...

点击上方↑↑↑蓝字关注我们~「2019 Python开发者日」全日程揭晓,请扫码咨询 ↑↑↑来源 | 宜信技术学院第1期技术沙龙-线上直播原标题为:AI中台:一种敏捷的智能业务支持方案导读:随着“数据中台”的提出和成功实践,各…