p7云原生架构师一期

如何提高DOCKER 的安全性

Docker容器多年来一直是开发人员工具箱的重要组成部分,通过 docker 以标准化的方式构建、分发和部署应用程序。随着容器技术的发展以及应用越来越广泛,其安全性问题也逐渐凸显出来。我们都知道容器其实就是运行在操作系统上的一个线程,通过 namespace 和 cgroup 技术与宿主机进行隔离。但这种隔离并非十分安全的,攻击者可以很容易利用参数的错误配置从容器内逃逸到宿主机上。

“容器”一词经常被误解,因为许多开发人员倾向于将隔离的概念与错误的安全感联系起来,认为这种技术本质上是安全的。

所谓的容器的安全,其实完全取决于以下几个方面:

  • 配套基础设施的安全(例如操作系统、基础设施平台)
  • 基础设施上的软件组件
  • 运行时环境及相关配置

目前有很多有关容器的最佳实践都包含了提高容器的安全性措施。这些措施大都围绕着镜像构建、资源特权管理、文件系统、网络等方面。为了长期维护容器安全,- Clair 、TrivyDocker Bench for Security 这些安全扫描工具不可或缺。

以下这些最佳实践的安全性措施仅限于对 Docker 的使用,并未包含任何 K8s 及 Docker Compose的理念与操作。但是对使用 K8s 或 Docker Compose 也有参考意义。

1. 镜像构建

1. 1 基础镜像的选择

基础镜像尽量选用可信的镜像,比如docker hub 的官方镜像。如果需要使用基础的发行版,则推荐使用 Alpine Linux 的基础镜像,因为他轻量级以及阉割部分功能,可以保证受攻击面比较小。当然使用alpine版本的镜像也有缺点,由于缺少linux部分依赖,则需要手动补全、安装软件的各种依赖,也有一定的使用门槛。

当然也可以使用 Google 所引入的 Distroless 镜像作为基础镜像。其最大的特点是仅仅包含程序以及其依赖项,并不包含标准 Linux 发行版中的包管理器、shell或其他任何程序。

这里有一个实验,分别基于 Apline 和 Distroless 作为基础镜像,来构建简单的 Hello World 的 Springboot 应用镜像。此处参考文章 使用哪个容器镜像——Distroless 还是 Alpine? 最终结论是:

镜像大小— 使用 Alpine 基础镜像编译的镜像为93.5 MB,而 distroless 镜像为139 MB。因此,与 distroless 镜像相比,Alpine 镜像更轻巧。
漏洞数量——Alpine 镜像共有:216 个漏洞(未知:0,低:106,中:79,高:27,关键:4)而 Distroless Image 共有:50 个漏洞(未知:0,低) : 30, 中: 6, 高: 9, 关键: 5)

1.2 使用非特权用户

在默认的情况下,容器内的进程是以 root(id=0)来运行程序的,这是一个十分危险的行为。所以需要通过以下两种方式来设定一个默认用户

  1. 在docker run时,指定一个容器中不存在的 user id docker run -u 4000 <image>
  2. 在 Dockerfile 中创建一个默认用户
FROM <base image>

RUN addgroup -S appgroup \
 && adduser -S appuser -G appgroup
 
USER appuser

... <rest of Dockerfile> ...
这种方案则需要关注基础镜像中使用何种工具来创建用户了

1.3 使用单独的用户ID命名空间

默认情况下,Docker守护进程使用主机的用户ID所在的命名空间。因此,当容器内权限提升后,则会以root方式来访问宿主机主机以及其他容器。 为了降低此风险,应该将主机和Docker守护程序配置为使用带有--userns remap选项的单独名称空间。

1.4 当心环境配置

在 Dockerfile中,ENV指令中不应包含任何敏感信息。例如:

ENV $VAR
RUN unset $VAR

即使这样做,$VAR其实仍在镜像中,且很容易被读取。

为了增加读取限制,应该在构建的每一层中,在引入环境变量后将其销毁:

RUN export ADMIN_USER="admin" \
    && do something \   
    && unset ADMIN_USER

1.5 不要将 docker.sock 暴露在容器中

这个不用多说了,由于Docker的 C/S 架构,docker.sock 是 Docker API的主要入口。如果放开这个入口,相当于把自家的大门完全敞开了。

2. 资源特权管理

特权是十分危险的,容器永远不应该以特权运行,否则容器中的进程将会拥有主机上的root权限及功能。docker 的创建应该增加--security-opt=no-new-privileges 参数来限制这种特权。

另一方面,Docker 利用linux的capabilities机制来进行细粒度的权限控制访问。容器会使用默认的一组capabilities,但是大部分我们基本都不会用到。一种建议是将所有的 capabilities 删除,仅根据程序需求来单独添加。例如运行 web 服务器的容器, 其实仅需要 NET_BIND_SERVICE 的能力,用于绑定服务器低于1024端口的权限(一般web服务器需要使用 80 端口)

第三,不要共享主机文件系统中的敏感目录:

  • root (/),
  • device (/dev)
  • process (/proc)
  • virtual (/sys) mount points. 如有需要,则谨慎的选择目录的权限设置

2.1 使用 Control Group 限制对资源的访问

控制组是用于控制每个容器对CPU、内存、磁盘I/O的访问的机制。默认情况下,容器与独立的cgroup相关联,但如果存在选项--cgroup parent,则会使主机资源面临DoS攻击的风险,因为这允许主机和容器之间共享资源。

3. 文件系统

3.1 使用只读权限

当容器是临时启用,且是无状态服务时,则应该将挂载的文件系统限制为只读。 docker run --read-only <image>

3.2 对非持久性数据使用临时目录

docker run --read-only --tmpfs /tmp:rw ,noexec,nosuid <image>

3.3 对持久化数据使用特定的文件系统

如果需要与主机文件系统或其他容器共享数据,则有两个选项:

  1. 创建挂载点,并对其使用限额进行限制。--mount type=bind,o=size
  2. 为专用分区创建绑定卷 --mount type=volume

在这两种情况下,如果共享数据不需要由容器修改,则使用 --read-only。 docker run -v <volume-name>:/path/in/container:ro <image>
或是 docker run --mount source=<volume-name>,destination=/path/in/container,readonly <image>

4. 网络

4.1 不要使用Docker的默认桥接docker0

docker0 是Docker用于将主机网络与容器网络分离的网桥。 当一个容器创建时,默认情况下Docker会将其连接到docker0网络。因此,所有容器都连接到docker0,并能够相互通信。这也将存在安全隐患。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值