天天看點

你可能不知道的容器鏡像安全實踐

作者:EdisonTalk
你可能不知道的容器鏡像安全實踐

大家好,我是Edison。

最近在公司搭建CI流水線,涉及到容器鏡像安全的話題,形成了一個筆記,分享與你,也希望我們都能夠提高對安全的重視。

時代背景

近年來應用程式逐漸廣泛運作在容器内,容器的采用率也是逐年上升。

根據 Anchore 釋出的《Anchore 2021年軟體供應鍊安全報告》顯示容器的采用成熟度已經非常高了,65% 的受訪者表示已經在重度使用容器了,而其他 35% 表示也已經開始了對容器的使用:

你可能不知道的容器鏡像安全實踐

是以,基于軟體的傳遞變成了基于容器鏡像的傳遞。

業界已經達成共識:雲原生時代已經到來,如果說容器是雲原生時代的核心,那麼鏡像應該就是雲原生時代的靈魂。鏡像的安全對于應用程式安全、系統安全乃至供應鍊安全都有着深刻的影響。

但是,容器的安全問題卻是大多數IT開發團隊所忽視的:

根據 snyk 釋出的 2020年開源安全報告 中指出,在 dockerhub 上常用的熱門鏡像幾乎都存在安全漏洞,多的有上百個,少的也有數十個。具體資料如下圖所示:

你可能不知道的容器鏡像安全實踐

不幸的是,很多應用程式的鏡像是以上述熱門鏡像作為基礎鏡像,進而将這些漏洞帶到了各自的應用程式中,增加了安全風險。

解決方式

GitLab建議我們:預防為主,防治結合的方式來提高鏡像的安全性。

所謂防,就是要在編寫 Dockerfle 的時候,遵循最佳實踐來編寫安全的Dockerfile;還要采用安全的方式來建構容器鏡像;

所謂治,即要使用容器鏡像掃描,又要将掃描流程嵌入到 CI/CD 中,如果鏡像掃描出漏洞,則應該立即終止CI/CD Pipeline,并回報至相關人員,進行修複後重新觸發 CI/CD Pipeline。

防的最佳實踐

(1)以安全的方式建構容器鏡像

正常建構容器鏡像的方式就是 docker build,這種情況需要用戶端要能和 docker守護程序進行通信。對于雲原生時代,容器鏡像的建構是在 Kubernetes 叢集内完成的,是以容器的建構也常用 dind(docker in docker)的方式來進行。

衆所周知,dind 需要以 privilege 模式來運作容器,需要将主控端的 /var/run/docker.sock 檔案挂載到容器内部才可以,否則會在 CI/CD Pipeline建構時收到錯誤。

為了解決這個問題,可以使用一種更安全的方式來建構容器鏡像,也就是使用 kaniko。

kaniko是谷歌釋出的一款根據 Dockerfile 來建構容器鏡像的工具。kaniko 無須依賴 docker 守護程序即可完成鏡像的建構。其和GitLab CI/CD的內建也是非常友善的,隻需要在GitLab CI/CD 中嵌入即可,下面是在我司CI Pipeline中的實踐:

variables:
  EXECUTOR_IMAGE_NAME: "gcr.io/kaniko-project/executor" 
  EXECUTOR_IMAGE_VERSION: "debug"
docker-build-job:
  stage: docker-build-stage
  image:
    name: "$EXECUTOR_IMAGE_NAME:$EXECUTOR_IMAGE_VERSION"
    entrypoint: [""]
  rules:
    - if: '$IMAGE_SOURCE_BUILD != "" &&$BUILD_DOCKER_IMAGE == "true" && $CI_PIPELINE_SOURCE !="merge_request_event"'
  script:
    - |- 
      KANIKO_CONFIG="{\"auths\":{\"$CI_REGISTRY_IMAGE\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}"
       echo "${KANIKO_CONFIG}" >/kaniko/.docker/config.json
    - mkdir release
    - cp -r Build/* release/
    - |
       /kaniko/executor \
         --context "${CI_PROJECT_DIR}" \
         --dockerfile "Dockerfile" \
         --destination"${CI_REGISTRY_IMAGE}:${BUILD_TAG}"           

(2)選擇合适且可靠的基礎鏡像

Dockerfile 的第一句通常都是 FROM some_image,也就是基于某一個基礎鏡像來建構自己所需的業務鏡像,基礎鏡像通常是應用程式運作所需的語言環境,比如.NET、Go、Java、PHP等,對于某一種語言環境,一般是有多個版本的。

我司主要使用的是.NET,而原生微軟的ASP.NET 6.0鏡像(mcr.microsoft.com/dotnet/aspnet:6.0)有5個Critical的安全漏洞,一般不建議采用。根據Global項目組的實踐,建議采用RedHat提供的.NET 6.0運作時鏡像,該鏡像由RedHat維護,定期在更新(最新更新是一周前),目前無Critical的安全漏洞。

你可能不知道的容器鏡像安全實踐

鏡像位址:https://catalog.redhat.com/software/containers/ubi8/dotnet-60-runtime/6182efaddd607bfc82e66343

docker pull registry.access.redhat.com/ubi8/dotnet-60-runtime:6.0-22           

(3)不安裝非必要的安裝包

Dockerfile 中應該盡量避免安裝不必要的軟體包,除非是真的要用到。比如:我們習慣了直接寫 apt-get update && apt-get install xxxx。

因為,安裝非必要的軟體包除了會造成鏡像體積的增大 也會 增加受攻擊的風險程度。

你可能不知道的容器鏡像安全實踐

(4)以非root使用者啟動容器

在 Linux 系統中,root使用者意味着超級權限,能夠很友善的管理很多事情,但是同時帶來的潛在威脅也是巨大的,用 root 身份執行的破壞行動,其後果是災難性的。在容器中也是一樣,需要以非root 的身份運作容器,通過限制使用者的操作權限來保證容器以及運作在其内的應用程式的安全性。在 sysdig 釋出的《Sysdig 2021年容器安全和使用報告》中顯示,58% 的容器在以 root 使用者運作。足以看出,這一點并未得到廣泛的重視。

是以,建議在Dockerfile中添加指令來讓容器以非root使用者身份啟動,在我司的CI Pipeline中的實踐:

......


USER 0
RUN chown -R 1001:0/opt/app-root && fix-permissions /opt/app-root
# No root should run
USER 1001
 
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
 
CMD dotnet ${APPLICATION_DLL}           

治的最佳實踐

在CI流水線中加入容器鏡像安全掃描任務

在 GitLab 中提供了容器鏡像分析器(Container-Scanning-Analyzer)來對生成的容器鏡像進行掃描,建議将其加入CI Pipeline中進行高頻率的檢查工作。

在我司的CI Pipeline中,內建了container-scanning-analyzer來掃描容器鏡像,如果掃描結果有Critical的漏洞,流水線會自動失敗,阻塞後續Job執行并發送Email提醒。下圖給出了一個簡單的示例(并非我司CI流水線完整流程):

你可能不知道的容器鏡像安全實踐

隻有當掃描結果不包含Critical的漏洞時,流水線才會被視為成功,進而允許後續操作,包括Merge開發分支到主幹等。

你可能不知道的容器鏡像安全實踐

參考資料

極狐:《GitLab DevSecOps七劍下天山之容器鏡像安全掃描》https://mp.weixin.qq.com/s/pnP0bjFdXlay42OGghUWNw

極狐:《雲原生時代,如何保證容器鏡像安全?》https://blog.csdn.net/weixin_44749269/article/details/123077566

繼續閱讀