Docker容器 关于镜像构建的安全问题
写在前面
确保容器中服务与应用安全是容器化演进的关键点。容器安全涉及到应用开发与维护的整个生命周期,本文主要从镜像构建的视角来看docker容器的一些安全问题及应对措施。
一、权限管理
1.避免以容器以root身份运行
在Openshift与k8s环境中默认容器需要以非root身份运行,使用root身份运行的情况很少,所以不要忘记在dockerfile中包含USER指令,以将启动容器时默认有效 的UID 更改为非 root 用户。
以非 root 身份运行需要在 Dockerfile 中做的两个步骤:
- 确保USER指令中指定的用户存在于容器内。
- 在进程将要读取或写入的位置提供适当的文件系统权限。
FROM alpine
#创建目录,添加myuser用户,目录所有作为myuser
RUN mkdir /server && adduser -D myuser && chown -R myuser /server
USER myuser
WORKDIR /server
COPY myapp ./
CMD ["./myapp"]
2.可执行文件权限应为root用户拥有但不可写
容器中的每个可执行文件都应该由 root 用户拥有,即使它由非 root 用户执行,并且不应该是全局可写的。
通过阻止执行用户修改现有的二进制文件或脚本,可以有效降低攻击,保证容器不变性。不可变容器不会在运行时自动更新其代码,通过这种方式,我们可以防止正在运行的应用程序被意外或恶意修改。
我们在使用COPY时
COPY --chown=myuser:myuser myapp ./
#应改为
COPY myapp ./
二、减少攻击面
避免加载不必要的包、第三方应用或暴露端口以减少攻击面。我们在镜像中包含的组件内容越多,容器暴露的就越多,维护起来就越困难。
1.采用多阶段构建
我们在《Dockerfile 多阶段构建实践》中说到采用多阶段构建,可以此降低构建复杂度,同时有效减小镜像尺寸。
在多阶段构建中,我们创建一个中间容器(阶段),其中包含编译工具及生成最终可执行文件。然后,我们只将生成的工件复制到最终镜像中,而无需额外的开发依赖项、临时构建文件等等。
精心设计的多阶段构建仅包含最终映像中所需的最少二进制文件和依赖项,而不包含构建工具或中间文件。它更为安全,并且还减小了镜像大小。可以有效减少了攻击面,减少了漏洞。
多阶段构建的实现请参考上篇文章《Dockerfile 多阶段构建实践》
2.使用可信赖的镜像
假如我们不是从头开始构建镜像,基镜像建立在不受信任或不受维护的镜像之上会将所有问题和漏洞从该镜像继承到您的容器中。
基础镜像选择的参考:
- 我们应该选择来自受信任仓库和经过验证的官方镜像。
- 使用自定义镜像时,我们应该检查镜像源和构建的 Dockerfile。更进一步,我们甚至应该以这个Dockerfile来构建自己的基础镜像。因为我们无法保证在dockerhub等公共仓库中发布的映像确实是从指定的 Dockerfile 构建的。也不能保证它是最新的。
- 有时候在安全性和极简主义方面考虑,官方镜像可能并不非合适的,最优解是我们自己从头构建属于自己的镜像。
2.从头开始构建镜像
假如如果你是从centos镜像开始构建,那么你创建的容器可能将会包含几十个或者上百个漏洞。所以构建一个安全的镜像我们最好需要知道我们的基镜像存在哪些威胁。在生产中通常会从Scratch空镜像或distroless开始。
distroless镜像仅包含应用程序及其运行时依赖项。它们不包括在标准 Linux 发行版中发布应用如包管理器、shell 或任何其他程序。Distroless 镜像非常小。最小的 distroless 图像gcr.io/distroless/static大约为 650 kB。只有alpine(约2.5 MB)大小的 四分之一 ,不到debian(50 MB)大小的 1.5% 。
FROM golang:1.13-buster as build
WORKDIR /go/src/app
ADD . /go/src/app
RUN go get -d -v ./...
RUN go build -o /go/bin/app
# 引用Distroless镜像
FROM gcr.io/distroless/base-debian10
COPY --from=build /go/bin/app /
CMD ["/app"]
gcr.io/distroless/base-debian10只包含一组基本的包,如包括只需要的库,如glibc、libssl和openssl 当然对于像 Go 这样不需要libc 的静态编译应用程序我们就可以替换为如下基镜像
FROM gcr.io/distroless/static-debian10
关于distroless基镜像的更多信息可以参考https://github.com/GoogleContainerTools/distroless
3.及时更新镜像
使用经常更新的基础镜像,在需要时重构你的镜像。随着新的安全漏洞不断被发现,坚持使用最新的安全补丁是一种通用的安全最佳实践。
版本控制策略:
- 坚持使用稳定或长期支持版本,这些版本会迅速提供安全修复程序。
- 提前计划。准备好在基本镜像版本达到生命周期结束或停止接收更新之前删除旧版本并迁移。
- 定期重建自己的镜像,从基础发行版、Node、Golang、Python 等获取最新的包。 大多数包或依赖项管理器,如npm或go mod,将提供指定版本最新的安全更新。
4.端口暴露
容器中每个打开的端口都是通往系统的大门。我们应该仅公开应用程序需要的端口,并且避免公开 SSH (22) 等端口。
我们知道 Dockerfile 提供了EXPOSE 命令有暴露端口,但是该命令仅用于提供信息和用于文档目的。运行容器时,容器不会自动允许所有 EXPOSE 端口的连接(除非在启动容器时使用docker run --publish-all)。
启动容器时,通过-P暴露的端口应与dockerfile中EXPOSE命令指定的端口一致,这样更便于维护。
三、敏感数据管理
1.凭证和密钥
禁止在 Dockerfile 指令(环境变量、参数或其他任何命令中)中放入凭据和密钥。
在复制文件到镜像时,即使文件在 Dockerfile 的后续指令中被删除,它仍然可以在之前的层上访问。因为镜像分层原理,你的文件并没有真正被删除,只是“隐藏”在最终文件系统中。因此在构建镜像时,我们应该遵循以下做法:
- 如果应用程序支持通过环境变量进行配置,我们可以通过docker run 中的
-e选项配置,或者使用Docker secrets、Kubernetes secrets提供值作为环境变量。 - 使用配置文件并在docker 中绑定挂载配置文件,或者使用Kubernetes secret 挂载。
关于secrets的使用会在后面文章中详细介绍。
2.ADD、COPY
ADD 和 COPY 指令在 Dockerfile 中提供类似的功能。但是COPY 更为明确。
除非我们确实需要 使用ADD 功能,例如从 URL 或从 tar 文件添加文件。不然最好使用 COPY,COPY 的结果更具可预测性且不易出错。
在某些情况下,最好使用 RUN 指令而不是 ADD 来下载使用curl或wget的包,解压缩然后删除原始文件,减少层数。
3.构建上下文与dockerignore
在构建时我们通常使用.作为上下文
#docker build -t images:v1 .
使用 .作为上下文时我们需要谨慎些,因为docker CLI会将上下文中机密或不必要的文件添加到守护进程,甚至到容器中,例如配置文件、凭据、备份、锁定文件、临时文件、源、子文件夹、点文件等等。
在比如:
COPY . /server
此时会将目录下所有内容都添加到镜像中,包括Dockfile本身。
所以正确做法是创建一个包含需要在容器内复制文件的文件夹,将其用作构建上下文,并在可能的情况下明确 COPY 指令(避免使用通配符)。例如:
#docker build -t images:v1 build_files/
为了排除不必要的文件,我们也可以创建一个.dockerignore文件,在其中明确排除的文件和目录。
以上是容器构建时常见安全问题与相关处理措施,容器安全涉及面广,遍布整个devops流程中。有兴趣的同学可以另外一个位面介入深究。
NEXT
- Docker容器secrets详解
- Docker容器减小镜像尺寸实践
希望小作文对你有些许帮助,如果内容有误请指正。
您可以随意转载、修改、发布本文章,无需经过本人同意。
Docker容器 关于镜像构建的安全问题的更多相关文章
- 7.云原生之Docker容器Dockerfile镜像构建浅析与实践
转载自:https://www.bilibili.com/read/cv15220707/?from=readlist Dockerfile 镜像构建浅析与实践 描述:Dockerfile是一个文本格 ...
- [转]图解Docker容器和镜像
本文转自:https://www.cnblogs.com/wangqiaomei/p/5818636.html 图解Docker容器和镜像 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器 ...
- 图解Docker容器和镜像
图解Docker容器和镜像 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别. 当我对Docke ...
- Docker容器和镜像的区别
docker容器和镜像区别 转自 https://www.cnblogs.com/bethal/p/5942369.html 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(conta ...
- 理解docker容器和镜像(layer,ufs)和docker命令解释
博客好文1:http://blog.csdn.net/x931100537/article/details/49633107(理解docker容器和镜像,理解简单,从原理入手,什么是layer,什么是 ...
- 【原创】深入理解Docker容器和镜像 -- 分析了docker的命令含义
10张图带你深入理解Docker容器和镜像 镜像(Image)就是一堆只读层(read-only layer)的统一视角 要点:容器 = 镜像 + 读写层.并且容器的定义并没有提及是否要运行容器. 一 ...
- docker容器和镜像区别
这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别. 当我对Docker技术还是一知半解的时候,我 ...
- docker之NGINX镜像构建
Nginx是一个高性能的Web和反向代理服务器,它具有很多非常优越的特性:1.作为Web服务器.2.作为负载均衡服务器.3.作为邮件代理服务器.4.安装及配置简单.接下来我们介绍在docker构建ng ...
- 10张图带你深入理解Docker容器和镜像
http://dockone.io/article/783 [编者的话]本文用图文并茂的方式介绍了容器.镜像的区别和Docker每个命令后面的技术细节,能够很好的帮助读者深入理解Docker. Doc ...
随机推荐
- python基础之多线程的操作
# 多线程实例# 例1.单线程from time import sleep,ctimedef task1(taskName): for i in range(2): print("正在执行 ...
- debug:am trace-ipc源码分析
debug:am trace-ipc源码分析 目录 debug:am trace-ipc源码分析 一.使用 官网介绍 命令提示 小结 二.源码分析 ActivityManagerShellComman ...
- 解决pip下载速度慢的问题
解决链接:https://blog.csdn.net/u013901768/article/details/82343512 感谢这位博主的分享. 博客内容如下: 1. 安装时强制使用国内源进行安装, ...
- (JAVA1)学习,复习的好帮手
学习准备:博客 博客,英文名Blog,它的正式名称为网络日记. 为什么要写博客 我们学习任何东西都需要总结和思考.有时候我们一直在赶路,却忘了放慢脚步 提升文笔组织能力 提升学习总结能力 提升逻辑思维 ...
- Centos LInux 7.0 内核3.1 升级简化流程
Centos LInux 7.0 内核3.1 升级建华流程 1)#导入ELRepo软件仓库的公共秘钥rpm --import https://www.elrepo.org/RPM-GPG-KEY-el ...
- Vue学习笔记(三)条件渲染和循环渲染
目录 一.条件渲染 1. v-if 2. 与v-else配合使用 3. 与v-else-if配合使用 4. v-show的使用 5. 类型切换案例 二.列表渲染 1. 遍历数组 2. 遍历对象 获取v ...
- 抄书抄博客毒害社区的Writer几时休?
曾几何时,博客园用户也渐渐分成了两类人:Writer和Coder. 何为Coder?就是认认真真写代码,平时分享工作中的一些问题.好的解决方案,或者写一些实用的原理技术文.答疑解惑的教程技术文. 何为 ...
- 身边好几个技术一般的程序员都面上了,阿里P7门槛降低?
经常在网上的论坛里看到讨论程序员的级别,尤其在跳槽类的信息里可以看到对标阿里P7,百度T6,腾讯3.1等字眼,似乎大厂的级别俨然可以成为业内的通用货币,类似于高考分数一样,哪一档就对应着什么样的待遇. ...
- GitHub标星8k,字节跳动高工熬夜半月整理的“组件化实战学习手册”,全是精髓!
前言 什么是组件化? 最初的目的是代码重用,功能相对单一或者独立.在整个系统的代码层次上位于最底层,被其他代码所依赖,所以说组件化是纵向分层. 为什么要使用组件化? 当我们的项目越做越大的时候,有时间 ...
- 第5篇-调用Java方法后弹出栈帧及处理返回结果
在前一篇 第4篇-JVM终于开始调用Java主类的main()方法啦 介绍了通过callq调用entry point,不过我们并没有看完generate_call_stub()函数的实现.接下来在ge ...