目录

遵从最佳实践,编写 Dockerfile

选择合适的基础镜像

以非 root 用户启动容器

采用多阶段构建

选择来源可靠且经常更新的镜像

用安全的方式构建容器镜像

使用容器镜像扫描

和极狐 GitLab workflow 的结合

极狐 GitLab DevSecOps 功能试用申请


Docker 的出现改变了应用程序的运行方式与交付模式:应用程序运行在容器内而软件的交付变成了容器镜像的交付 。随着这几年云原生的火热,容器的采用率也是逐年上升。根据 Anchore 发布的《Anchore 2021 年软件供应链安全报告》显示容器的采用成熟度已经非常高了,65% 的受访者表示已经在重度使用容器了,而其他 35% 表示也已经开始了对容器的使用:

但是容器的安全问题却不容乐观,这个可以在公众号文章极狐GitLab DevSecOps 七剑下天山之容器镜像安全扫描查看详情。

由于容器是由容器镜像生成的,如何保证容器的安全,在很大程度上取决于如何保证容器镜像的安全。而对于容器镜像安全的保证,可以秉承预防为主,防治结合的理念来进行。所谓防,就是要在编写 Dockerfle 的时候,遵循最佳实践来编写安全的 Dockerfile;还要采用安全的方式来构建容器镜像;所谓治,即要使用容器镜像扫描,又要将扫描流程嵌入到 CI/CD 中,如果镜像扫描出漏洞,则应该立即终止 CI/CD Pipeline,并反馈至相关人员,进行修复后重新触发 CI/CD Pipeline

下面就从即防又治的角度来讲述如何确保容器镜像安全。

遵从最佳实践,编写 Dockerfile

选择合适的基础镜像

Dockerfile 的第一句通常都是 FROM some_image,也就是基于某一个基础镜像来构建自己所需的业务镜像,基础镜像通常是应用程序运行所需的语言环境,比如 Go、Java、PHP 等,对于某一种语言环境,一般是有多个版本的。以 Golang 为例,即有 golang:1.12.9,也有 golang:1.12.9-alpine3.9,不同版本除了有镜像体积大小的区别,也会有安全漏洞数量之别。上述两种镜像的体积大小以及所包含的漏洞数量(用 trivy 扫描)对比如下:

可以看到 golang:1.12.9-alpine3.9golang:1.12.9 有更小的镜像体积(351MB vs 814MB),更少的漏洞数量(24 vs 1306)。所以,在选取基础镜像的时候,要做出正确选择,不仅能够缩小容器镜像体积,节省镜像仓库的存储成本,还能够减少漏洞数量,缩小受攻击面,提高安全性。

以非 root 用户启动容器

在 Linux 系统中,root 用户意味着超级权限,能够很方便的管理很多事情,但是同时带来的潜在威胁也是巨大的,用 root 身份执行的破坏行动,其后果是灾难性的。在容器中也是一样,需要以非 root 的身份运行容器,通过限制用户的操作权限来保证容器以及运行在其内的应用程序的安全性。在 Dockerfile 中可以通过添加如下的命令来以非 root 的身份启动并运行容器:

RUN addgroup -S jh && adduser -S devsecops -G jhUSER devsecops

上述命令创建了一个名为 jh 的 Group,一个名为 devsecops 的用户,并将用户 devsecops 添加到了 jh Group 下,最后以 devsecops 启动容器。

sysdig 发布的《Sysdig 2021 年容器安全和使用报告》中显示,58% 的容器在以 root 用户运行。足以看出,这一点并未得到广泛的重视。

不安装非必要的软件包

很多用户在是编写 Dockerfile 的时候,习惯了直接写 apt-get update && apt-get install xxxx,网上也有很多这样的例子(包括 GitHub)。用户需要清楚 xxx 这个包是否真的要用,否则这种情况会造成镜像体积的变大以及受攻击面的增加。

ubuntu:20.04 为例来演示安装 vim curl telnet 这三个常用软件包,给镜像体积以及漏洞数量带来的影响:

可以看出,因为安装了 vim curl telnet 这三个常见的软件包,导致镜像体积增加了一倍(从 72.4MB 到 158MB),漏洞数量翻了接近一番(从 60 到 119)。因此,在编写 Dockerfile 的时候,一定要搞清楚哪些包是必须安装的,而哪些包是非必需安装的。不要认为 apt-get install 使用起来很爽就都安装。

针对其他操作系统的包管理器存在同样的问题,诸如 apk add,yum install 等。

采用多阶段构建

多阶段构建不仅能够对于容器镜像进行灵活的修改,还能够在很大程度上减小容器镜像体积,减少漏洞数量(这个第一点有异曲同工之妙)。

选择来源可靠且经常更新的镜像

由于镜像构建的灵活性和便捷性,任何一个人都可以构建容器镜像并推送至 Dockerhub 供其他人使用。所以在搜索某一个镜像的时候,会出现很多类似的结果,这时候就需要仔细辨别:镜像是否有官方提供的,镜像是否一直有更新,镜像是否可以找到对应的 Dockerfile 来查看到底是如何构建的。信息不全且长时间无更新的镜像,其安全性无法得到保证,不应该使用此类镜像,这时候可以选择自己使用这些规则来构建可用的安全镜像。

当然,除此以外,还有很多编写 Dockerfile 的最佳时间,诸如不要把敏感信息编写在 Dockerfile 并构建在镜像中,避免敏感信息造成泄漏;要用工具(如 Hadolint)来对 Dockerfile 进行扫描,以发现 Dockerfile 编写过程中的一些问题等等。

良好的 Dockerfile 编写习惯是保证容器镜像安全的第一步,接下来还需要用安全的方式来构建容器镜像。

用安全的方式构建容器镜像

常规构建容器镜像的方式就是 docker build,这种情况需要客户端要能和 docker 守护进程进行通信。对于云原生时代,容器镜像的构建是在 Kubernetes 集群内完成的,因此容器的构建也常用 dind(docker in docker)的方式来进行。比如在前面所有文章的 Demo 演示中,镜像的构建通常用如下代码:

build:image: docker:lateststage: buildservices:- docker:20.10.7-dindscript:- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY- docker build -t $CI_REGISTRY_IMAGE:1.0.0 .- docker push $CI_REGISTRY_IMAGE:1.0.0

众所周知,dind 需要以 privilege 模式来运行容器,需要将宿主机的 /var/run/docker.sock 文件挂载到容器内部才可以,否则会在 CI/CD Pipeline 构建时收到如下错误:

因此在使用自建 Runner 的时候,往往都需要挂在 /var/run/docker.sock,诸如在使用 K3s 来运行极狐 GitLab Runner 的时候,就需要在 Runner 的配置文件中添加以下内容:

[[runners.kubernetes.volumes.host_path]]name = "docker"mount_path = "/var/run/docker.sock"host_path = "/var/run/docker.sock"

为了解决这个问题,可以使用一种更安全的方式来构建容器镜像,也就是使用 kaniko。

使用 Kaniko 来构建容器镜像

Kaniko是谷歌发布的一款根据 Dockerfile 来构建容器镜像的工具。Kaniko 无须依赖 docker 守护进程即可完成镜像的构建。其和极狐 GitLab CI/CD 的集成也是非常方便的,只需要在极狐 GitLab CI/CD 中嵌入如下代码即可:

build:stage: buildtags:- k3simage:name: registry.jihulab.com/jh-xiaomage-devops/go-demo/kaniko:debugentrypoint: [""]script:- mkdir -p /kaniko/.docker- echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json- >-/kaniko/executor--context "${CI_PROJECT_DIR}"--dockerfile "${CI_PROJECT_DIR}/Dockerfile"--destination "${CI_REGISTRY_IMAGE}:1.0.0"

代码块说明:

  • image.name:kaniko 的镜像名称,官方镜像为 gcr.io/kaniko-project/executor:debug,本文为了加速构建过程,将此镜像托管在极狐 GitLab SaaS 上,地址如上述代码块所示;

  • script:首先需要创建一个 /kaniko/.docker 目录,用来存放登录容器镜像仓库所需的凭证,接下来就是将镜像仓库的登录凭证以 config.json 的格式存放在 /kaniko/.docker 目录下,最后使用 /kaniko/exector 命令来构建容器镜像。

CI/CD Pipeline 的构建日志如下:

上述整个过程是在用 K3s 拉起的极狐 GitLab Runner 实例上面运行的此次构建,Runner 的信息可以在 Project --> Settings --> CI/CD --> Runners 里面看到:

在构建日志中也可以看到,此次构建是在 K3s 上运行的 Runner 上进行的:

而用 K3s 来安装极狐 GitLab Runner 的配置文件如下:

gitlabUrl: "https://jihulab.com/"
runnerRegistrationToken: "jh-gitlab-runner-token"
concurrent: 10
checkInterval: 30
logLevel: inforbac:create: truemetrics:enabled: falserunners:config: |[[runners]][runners.kubernetes]namespace = "{{.Release.Namespace}}"image = "ubuntu:20.04"name: k3s-runnertags: "jh,k3s,runner"

其中并没有 /var/run/docker.sock 相关的配置。这说明使用 kaniko 来构建容器镜像,并不需要与 docker 守护进程进行通信,所以是以一种更安全的方式完成了容器的构建。

关于如何使用 K3s 来拉起极狐 GitLab Runner 实例的内容可以查看文章用 K3s 来安装和运行极狐GitLab Runner。

使用容器镜像扫描

在遵从最佳实践编写 Dockerfile、用 Kaniko 构建容器之后,还需要对容器镜像做安全扫描,进一步确保容器镜像安全。而极狐 GitLab 有开箱即用的 DevSecOps 功能,其中就包含容器镜像扫描,关于详细的原理可以查看文章极狐GitLab DevSecOps 之容器镜像安全扫描。

可以很容易的在极狐 GitLab CI/CD 中把容器镜像扫描集成进去,以下几行简单命令就可以实现:

include:- template: Security/Container-Scanning.gitlab-ci.ymlcontainer_scanning:stage: testtags:- k3svariables:DOCKER_IMAGE: $CI_REGISTRY_IMAGE:1.0.0

参数说明:

  • include:极狐 GitLab CI/CD 关键字,用来将一些“通用”的 CI/CD 代码以 template 的形式在 CI/CD Pipeline 中使用,对于用户来讲使用时非常方便的,而且还能够让整体 CI/CD 的代码行数得到有效控制,易读性也增加了。

  • tags:指定此次构建需要在哪个 Runner 上执行;

  • variables.DOCKER_IMAGE:指定需要扫描的容器镜像;

触发 CI/CD Pipeline 之后,可以看到构建日志:

最后可以在极狐 GitLab 的 Security Dashboard 中看到扫描报告:

和极狐 GitLab workflow 的结合

可以很容易的将镜像构建、镜像扫描集成到极狐 GitLab CI/CD Pipeline 中,代码如下:

services:- docker:20.10.7-dindstages:          - build- testbuild:stage: buildtags:- k3simage:name: registry.jihulab.com/jh-xiaomage-devops/go-demo/kaniko:debugentrypoint: [""]script:- mkdir -p /kaniko/.docker- echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json- >-/kaniko/executor--context "${CI_PROJECT_DIR}"--dockerfile "${CI_PROJECT_DIR}/Dockerfile"--destination "${CI_REGISTRY_IMAGE}:1.0.0"include:- template: Security/Container-Scanning.gitlab-ci.ymlcontainer_scanning:stage: testtags:- k3svariables:DOCKER_IMAGE: $CI_REGISTRY_IMAGE:1.0.0

接下来只要提交 MR,就会触发构建流程,扫描的结果会显示在 MR 中:

点击相应的 CVE 就能够直接创建 issue 来对此问题进行跟踪:

等研发人员根据 issue 进行相应的修复之后,再次提交 MR 会继续看到扫描结果,以查看修复是否成功,如果成功修复,则可合并此 MR。

这种将镜像安全扫描嵌入到 CI/CD 中,能够做到持续自动化;将安全与研发工作流结合起来,能够做到安全漏洞可视化,方便研发人员第一时间修复漏洞,做到了真正的“安全左移”。这也是极狐 GitLab 一体化 DevSecOps 的真正优势:助力用户在一个平台上高效、安全的交付软件

极狐 GitLab DevSecOps 功能试用申请

DevSecOps 功能是极狐 GitLab 旗舰版专属的,但是用户可以申请免费试用。

选择一个想要使用 DevSecOps 功能的 Group,点击左侧导航栏中的安全,可以看到如下界面并点击开始免费使用

在出现的表单中输入相应的信息,点击继续

点击开始免费试用即可:

接着就可以看到 DevSecOps 功能已经开启:

云原生时代,如何保证容器镜像安全?相关推荐

  1. 云原生时代下的容器镜像安全(上)

    大家好,我是张晋涛. Kubernetes 作为云原生的基石,为我们带来了极大的便利性,越来越多的公司也都将 Kubernetes 应用到了生产环境中.然而,在享受其带来的便利性的同时,我们也需要关注 ...

  2. 云原生时代下,容器安全的“四个挑战”和“两个关键”

    作者 | 匡大虎 来源 | 阿里巴巴云原生公众号 云原生进程中的容器安全挑战 云原生的火热带来了企业基础设施和应用架构等技术层面的革新,在云原生的大势所趋下,越来越多的企业选择拥抱云原生,在 CNCF ...

  3. 云原生时代,谁是容器的最终归宿?

    前言 "云原生技术有利于各组织在公有云.私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用.云原生的代表技术包括容器.服务网格.微服务.不可变基础设施和声明式 API.这些技术能够构 ...

  4. 云原生时代到来 KubeSphere要让容器应用零门槛落地

    如今,Kubernetes.DevOps.微服务.应用管理等已经给客户带来了太多认知上的负担,而KubeSphere要做的就是简化.简化.简化. 云计算技术应用的深入,云原生正在成为云计算2.0的发展 ...

  5. 云原生时代, 选择.NET Core

    在容器.Kubernetes.DevOps,以及微服务等技术的推动下,2020年云原生势不可挡. .NET Core 也非常契合 云原生对应用运行时的不同需求,.NET Core和kubernetes ...

  6. 快进键启动,一文带你了解云原生时代容器安全

    简介: 分享阿里云容器安全的治理能力与经验,致力保护生产环境安全. 都说国内需求离容器化还远,更谈不上关注安全,喊的热闹而落地困难.但总得有些声音面向未来向前看. 在2020年Forrester Ia ...

  7. 云原生时代(五):Kubernetes与容器编排之战

    上文我们主要介绍了容器和Docker,第五部分我们来讲Kubernetes与容器编排之战. 容器编排与Kubernetes 在单机上运行容器,无法发挥它的最大效能,只有形成集群,才能最大程度发挥容器的 ...

  8. 为什么说容器的崛起预示着云原生时代到来?

    摘要:聊云原生之前,我们不妨从容器技术说起. 近年来,云原生(Cloud Native)可谓是 IT 界最火的概念之一,且随着云计算普及进程的不断加深,有愈演愈烈的趋势.今天再谈云原生已经不是少数几个 ...

  9. 杉岩:云原生时代,容器持久化存储方案选对了吗?

    75%的企业部署容器应用,云原生正加速业务创新 互联网的发展.云计算的出现,对传统IT带来冲击和挑战.面对数据爆炸式增长.业务种类增加.应用复杂性提高.软件迭代速度加快等一系列挑战,容器技术带来了突破 ...

最新文章

  1. php中$sum,如何在PHP中显示SUM列
  2. sleep 与 wait 区别
  3. 【struts2】struts2实现自定义数据类型转换器
  4. lamp ci框架 php配置文件,LAMP环境搭建
  5. 新生周赛:小青的宿舍(C语言)
  6. pcie usb3.0 驱动 for linux_微软WSL——Linux桌面版未来之光
  7. 2017.3.18 糖果 思考记录
  8. 同一个网址,根据pc跟移动端,跳到对应页面
  9. 只能用光盘启动怎么办?
  10. java错误找不到符号怎么办_java错误找不到符号
  11. 这世界没有能够预测未来的魔法水晶球
  12. access无法 dolby_dolby access怎么用?
  13. Windows 7精简版(2019.04.10)
  14. HDU5442(字符串的最大表示法或者后缀数组)
  15. Mysql(3):事务、锁及锁级别
  16. 简单的几句PHP生成美团3周年砸金蛋抽奖代码
  17. 戴尔T640服务器使用nvidia显卡风扇转速百分之百解决方案
  18. 11_JavaScript数据结构与算法(十一)树
  19. 五种基于 MapReduce 的并行计算框架介绍及性能测试
  20. 北斗导航 | SLAM之三维激光雷达点云(附Matlab源代码)

热门文章

  1. 用蚕茧表示法写简洁实用的接口文档
  2. 开发调试指令大全--(博通开发调试命令)
  3. Android开发学习之路--UI之简单聊天界面
  4. 安装igraph踩过的坑
  5. SQL注入(基于 tryhackme 的讲解)
  6. RandomAccess接口使用
  7. DFS实现传教士野人渡河问题
  8. Complex Multiplier IP 使用教程(源码)
  9. 《萌小甜动图字帖》使用简介
  10. Observability:Wavefront