生产中的12种容器镜像扫描最佳实践
現在很多團隊面臨著這么一個挑戰:如何在不減慢應用交付速度的情況下,管理好安全風險。有種方法可以解決該問題,就是采用安全的 DevOps 工作流程。
安全的DevOps(也稱為DevSecOps)會在從開發到生產的整個應用程序聲明周期中提供安全性以及監控功能,幫助我們交付安全、穩定和高性能的應用程序。如果我們把該工作流程插入現有的工具鏈中,可以為DevOps、開發人員和安全團隊最大程度地提高效率。
DevSecOps五個基本工作流程包括鏡像掃描、運行安全、合規性、Kubernetes和容器的監控以及應用程序和云服務監控。
鏡像掃描是嵌入到DevSecOps工作流程中的一項關鍵功能。作為第一道防線,它可以幫助我們在漏洞被利用之前檢測到漏洞并阻止,另外,它還易于實現并可自動化。本文將介紹多個鏡像掃描的最佳實踐和技巧,幫助大家采用有效的容器鏡像掃描策略。
什么是容器鏡像掃描
鏡像掃描是指分析容器鏡像的內容和構建過程,以檢測安全問題、漏洞或錯誤實踐。
我們可以從多個 Feed(NVD、Alpine、Cannonical等)中收集"通用漏洞披漏(CVE)"信息,以檢查鏡像是否容易受到攻擊,其中有些還提供了開箱即用的掃描規則,以查找最常見的安全問題和錯誤實踐。
鏡像掃描可以輕松被集成到DevSecOps工作流程的多個步驟中,例如集成到CI/CD管道中,阻止漏洞到達注冊表;集成在注冊表中,防止第三方鏡像中的漏洞;也可以在運行時集成,防止新發現的CVE。
當我們遵循并執行最佳實踐時,鏡像掃描可確保團隊不會因部署應用程序而導致交付速度變慢?,F在讓我們深入了解這 12 種鏡像掃描最佳實踐。
12 種鏡像掃描最佳實踐
1.將鏡像掃描嵌入到 CI/CD 管道中
構建容器鏡像時,我們應格外小心,并在鏡像發布之前對其進行掃描。我們可以在DevOps工作流程中使用已經構建好的CI/CD管道,并增加鏡像掃描的步驟。
CI/CD管道上鏡像掃描的架構如下:
測試和構建鏡像后,我們不必將鏡像推送到生產庫,可以先推送到暫存庫,然后運行鏡像掃描工具。這些工具通常會返回一份報告,列出發現的問題,并為每個問題分配不同級別的嚴重性。在CI/CD管道中,我們可以檢查這些鏡像掃描結果,并在出現任何嚴重問題時停止構建。
有一點要記住,自動化是最關鍵的,這是DevOps的核心概念,也適用于保護 DevOps。通過自動化 CI/CD 管道中的安全性檢查,我們可以在漏洞進入注冊表之前就將其捕獲,這樣就不會給漏洞任何機會。
2.采用 inline 掃描以保護隱私
在上一步中,我們看到了 CI/CD 管道中的鏡像掃描與臨時注冊表的關系,但如果鏡像包含一些錯誤憑證該怎么辦?它們可能會出現錯誤,最終導致數據泄漏。所以更進一步,我們可以實施 inline 鏡像掃描,不使用暫存庫,直接從 CI/CD 管道掃描鏡像。
使用 inline 鏡像掃描,它會僅掃描發送到掃描工具的元數據,從而幫助保護隱私。
使用 inline 鏡像掃描,可以在 CI/CD 管道內掃描鏡像。代碼推送后,在不離開 CI/CD 管道的情況下即可構建并掃描鏡像。鏡像元數據會被發送到鏡像掃描器,然后結果會發送回 CI/CD 管道。如果該鏡像遵循安全策略,那它將被推送到生產鏡像存儲庫。
3.在注冊表中執行鏡像掃描
開始實施鏡像掃描時,要將其嵌入到注冊表中,這是第一步。
通常,我們會有一個用于發布鏡像的專用存儲庫,還有一些用于從第三方下載鏡像的公共存儲庫。我們需要掃描這兩個存儲庫的鏡像。
我們部署的所有鏡像都將從注冊表中提取。通過在那里掃描鏡像,我們至少可以確保它們在運行之前已經被掃描過了。
4.使用 Kubernetes 準入控制器
如果想在使用鏡像之前先對其進行檢查,以防止將未掃描或易受攻擊的鏡像部署到集群上,我們可以使用準入控制器。
Kubernetes準入控制器是強大的Kubernetes原生功能,可以幫助我們自定義在集群上允許運行的內容。在對請求進行身份驗證和授權之后,在對象持久化存儲etcd之前,準入控制器可以攔截并處理對Kubernetes API的請求。
將Deployment的創建請求發送到Kubernetes后,準入控制器將調用Webhook并發送鏡像元數據。鏡像掃描器會將掃描結果發揮準入控制器,準入控制器在掃描通過后才會保留Deployment。
掃描工具通常會提供一個驗證Webhook,該Webhook可以按需觸發鏡像掃描,然后返回驗證決策。
準入控制器可以在調度鏡像之前調用此Webhook。Webhook返回的安全性驗證決策將傳回API服務器,該服務器會回復原始請求者,并且僅在鏡像通過檢查后才將對象持久保存在etcd數據庫中。不過,該決定是由鏡像掃描器做出的,并沒有任何集群中有關情況的上下文(context),這里我們可以使用OPA改進。
Open Policy Agent(OPA)是一種開放源代碼通用策略引擎,它是一種叫Rego的高級聲明性語言編寫的。OPA關鍵思想之一是將決策與政策執行脫鉤。
使用OPA,我們可以在Kubernetes集群而不是鏡像掃描器中做出準入決定,這樣就可以在決策過程中使用集群信息,例如命名空間、Pod元數據等。
5.固定的鏡像版本
有時,我們掃描的鏡像與在 Kubernetes 集群中部署的鏡像不同,例如在使用可變標簽(比如“latest”或“staging”)時,就可能會發生這種情況。此類標簽會不斷更新版本,從而使得我們很難知道最新的掃描結果是否仍然有效。
標簽“:latest”會指向鏡像的最新版本,除了最后一個版本外,其他所有版本均會被掃描。
使用可變標簽可能會導致使用同一個鏡像卻部署了不同版本容器的情況。除了掃描結果帶來的安全問題外,這可能還會導致難以調試。
為了代替 ubuntu:focal,我們應該盡可能使用不可變標簽,例如 ubuntu:focal-20200423。
這里要記住,version 標簽(對于某些鏡像)往往會進行較小的、不間斷的更新,因此確保可重復性唯一的選擇就是使用實際鏡像 ID:
ubuntu:@sha256:d5a6519d9f048100123c568eb83f7ef5bfcad69b01424f420f17c932b00dea76這里可能有點超出鏡像掃描最佳實踐的范圍,但我們要知道,這些不僅會影響 Dockerfiles 中的 FROM 命令,還會影響 Kubernetes Deployment 文件以及幾乎所有放置鏡像名稱的地方。
從鏡像掃描的角度來看,我們能做什么?我們可以通過結合使用 Kubernetes 準入控制器、鏡像掃描器和 OPA 引擎來實施此策略。
6.掃描操作系統漏洞
作為一般的鏡像掃描最佳實踐,請牢記這一點:"鏡像越輕越好",越輕的鏡像意味著更快的構建、更快的掃描以及更少的潛在漏洞依賴性。
新的Docker鏡像通常是在現有基礎鏡像的基礎上構建的,或在現有基礎鏡像之上添加一層。該基礎鏡像是由Dockerfile鏡像中的FROM語句定義的,這樣的分層的體系結構設計,可以在最常見的任務中節省大量時間。例如,在鏡像掃描時,我們只需要掃描一次基礎鏡像。如果父鏡像易受攻擊,那么在該父鏡像之上構建的任何其他鏡像也是易受攻擊的。
WordPress和PHP鏡像基于Apache,而Apache基于Ubuntu鏡像。如果Apache上存在漏洞,那么WordPress和PHP鏡像都將容易受到攻擊。
即使我們沒有在鏡像中引入新漏洞,但基礎鏡像中的漏洞也很容易讓我們受到攻擊。這就是為什么掃描工具應主動跟蹤已知漏洞文件的漏洞源,并在我們使用其中的漏洞時進行通知。
7.使用 distroless 鏡像
distroless 鏡像僅允許我們將應用程序及其依賴項打包在輕量級容器鏡像中。將運行時容器中的內容嚴格限制為必需內容可以最大程度地減少攻擊面。另外,它還可以改善掃描器的信噪比(例如CVE),并根據需要減輕負擔。
以下示例顯示了用于"Hello world"應用程序的Dockerfile,該應用程序在Ubuntu和Distroless上運行。
FROM ubuntu:focal
COPY main /
ENTRYPOINT ["/main"]
對其進行掃描后,我們發現了24個操作系統漏洞,其中兩個嚴重程度為中。這樣一個簡單的應用程序,影響大小居然有77.98MB這么大。
現在,基于distroless鏡像的同一應用程序:
FROM gcr.io/distroless/static-debian10
COPY main /
ENTRYPOINT ["/main"]
現在,我們只發現了兩個可以忽略不計的漏洞,此外,鏡像大小減小到只有6.93MB,這更適合此應用程序。
這表明,distroless容器沒有任何不必要的程序包,這些程序包可能導致更多的漏洞而被利用。
8.掃描第三方庫中的漏洞
應用程序使用了大量的庫,以至于這些庫最終提供的代碼行比團隊編寫的實際代碼還多。這意味著我們不僅需要知道代碼中的漏洞,還需要知道其所有依賴項中的漏洞。
不過掃描器檢測操作系統漏洞的相同漏洞源,會跟蹤這些漏洞,但并非所有工具都能像掃描鏡像中的庫一樣深入,因此請確保鏡像掃描器已深入挖掘并向我們警告這些漏洞。
9.優化鏡像層順序
謹慎使用Dockerfile中的RUN命令可以進一步優化鏡像。RUN命令的順序可能會對最終鏡像產生重大影響,因為它決定了鏡像層的順序。
我們可以首先防止較大的層(通常是不變的),最后放置變化最大的文件(例如已編譯的應用程序)來優化Docker緩存的使用。這將有利于現有層的使用,加快構建鏡像的速度,并間接加快鏡像掃描的速度。
10.掃描 Dockerfile 中的配置錯誤?
如我們所見,Docker鏡像構建過程遵循Dockerfile指令清單。
我們可以遵循以下幾種Dockerfile最佳實踐來檢測常見的安全性錯誤配置:
- 以特權(root)用戶身份運行,會訪問比所需更多的資源。
- 暴露不安全的端口,例如不應在容器上打開ssh 22端口。
- 由于錯誤的"COPY"或"ADD"命令不小心嵌入了私人文件。
- 不通過環境變量或安裝注入(泄露)secret或憑據。允許用戶將選項傳遞給Entrypoint和CMD是一個好習慣。
- 特定策略定義許多其他內容,例如被阻止的軟件包、允許的基本鏡像、是否已添加SUID文件等。
在這樣的Dockerfile中:
FROM ubuntu:focal-20200423 USER root RUN curl http://my.service/?auth=ABD54F0356FA0054 EXPOSE 80/tcp EXPOSE 22/tcp ENTRYPOINT ["/main"]我們的鏡像掃描可以自動檢測到以下問題:
USER root
我們以root身份運行:
EXPOSE 22/tcp
在這里,我們將暴露通常用于ssh的22端口,這是容器不應該包含的工具。另外, 我們還將公開90端口,但這個沒問題,這就像HTTP服務器的通用端口一樣。
?
RUN?curl?http://my.service/auth=ABD54F0356FA005432D45D0056AF5400
此命令使用一個auth密鑰,任何人都可以使用它來給我們造成危險,因此我們應該改用某種變量。這樣的密鑰不僅可以在Dockerfile上,還可以在鏡像中存在的任何文件中使用正則表達式進行檢測。作為一項額外措施,我們還可以檢查已知可存儲憑證的文件名。
11.快速標記 Kubernetes Deployment 中的漏洞
通過掃描的鏡像并不是絕對安全的。如果在掃描后,我們部署了鏡像,但此時發現了一個新漏洞,雖然我們可以立即加強安全策略,但是那些已經運行的鏡像會怎么樣?
生產中部署的易受攻擊鏡像的時間軸:
因此我們要連續掃描鏡像以達到以下目標:
- 檢測新漏洞并進行符合我們策略的更改。
- 向相應團隊報告這些發現,以便他們盡快修復鏡像。
?
當然,實施運行時掃描可以幫助我們減少這些漏洞的影響。以 CVE-2019-14287 為例,?我們可以編寫一些 Falco 規則來檢測該漏洞是否已被利用,但為每個漏洞編寫特定規則是一項耗時的工作,應將其作為最后一道防線。因此,我們要連續掃描集群中正在運行的鏡像。
安全工具會使用不同的策略進行存檔,最簡單的方法是每隔幾個小時重新掃描一次所有鏡像。理想情況下,我們希望在漏洞列表更新后立即重新掃描受影響的鏡像。不過某些工具能夠存儲鏡像元數據,無需完全重新掃描即可檢測新漏洞。
一旦在運行中的容器中發現漏洞,我們就應盡快修復。這里的關鍵是有效的漏洞報告,這樣每個人都可以專注于有關的信息。
實現此目標的一種方法是使用可查詢的漏洞數據庫,該數據庫讓 DevOps 安全團隊可以在其龐大的鏡像、程序包和 CVE 目錄中進行一些排序。他們將搜索諸如 CVE age、是否有可用的修復程序、軟件版本等參數。最后,如果可以下載這些報告并與漏洞管理團隊、CISO 共享,那么將非常有用。
讓我們用一個例子來說明,現在有一個查詢包含了以下內容:所有的漏洞在 prod 命名空間,嚴重性為高,CVE>30 天,修復可用。
借助這種漏洞報告功能,團隊可以輕松地識別出他們可以實際修復的易受攻擊鏡像,并在漏洞被利用之前開始著手解決方案。
12.選擇基于 SaaS 的掃描解決方案
在本地解決方案上選擇基于 SaaS 的掃描服務有很多好處:
- 按需擴展資源:我們可以首先掃描幾個鏡像,然后隨著容器應用程序的擴展而增長,無需擔心后端數據管理。
- 快速實施:我們可以將掃描嵌入到CI/CD管道中,并在幾分鐘內啟動并運行,而本地應用程序則需要更多時間來安裝和設置。
- 輕松升級和維護:SaaS提供商處理并推出補丁程序不需要我們手動升級或更新新功能。
- 無需基礎設施或人員成本:我們可以避免為擁有永久所有權的內容硬件和軟件許可證付費,也不需要現場維護和支持應用程序。
?
結論
鏡像掃描是DevSecOps工作流程中的第一道防線。通過使其自動化,我們可以最大程度地發揮其潛力,并在問題有機會變大之前發現問題。遵循鏡像掃描最佳實踐將幫助大家將安全性嵌入其中,并且交付速度不會因此降低。
最后,要記住鏡像掃描不該僅用一次,而要在工作流程中連續檢查,包括在構建時、在注冊表上、在部署之前以及容器已經運行時。
原文鏈接:https://sysdig.com/blog/image-scanning-best-practices/
?
總結
以上是生活随笔為你收集整理的生产中的12种容器镜像扫描最佳实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: expect批量执行命令
- 下一篇: java验证生日的正则表达式