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.9 比 golang: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:latest  stage: build  services:    - docker:20.10.7-dind  script:    - 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: build  tags:    - k3s  image:    name: registry.jihulab.com/jh-xiaomage-devops/go-demo/kaniko:debug    entrypoint: [""]  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: "Qif-fakrBBwzXnLUUaxv"concurrent: 10checkInterval: 30logLevel: inforbac:  create: truemetrics:  enabled: falserunners:  config: |    [[runners]]      [runners.kubernetes]        namespace = "{{.Release.Namespace}}"        image = "ubuntu:20.04"  name: k3s-runner  tags: "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: test  tags:    - k3s  variables:    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: build  tags:    - k3s  image:    name: registry.jihulab.com/jh-xiaomage-devops/go-demo/kaniko:debug    entrypoint: [""]  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: test  tags:    - k3s  variables:    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 性能曾经开启: