云原生时代,如何保证容器镜像安全?
目錄
遵從最佳實踐,編寫 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.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: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 功能已經開啟:
總結
以上是生活随笔為你收集整理的云原生时代,如何保证容器镜像安全?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Wordpress主题】Sakurai
- 下一篇: 一:部署harbor镜像仓库