歡迎光臨
每天分享高質量文章

10 個 Docker 鏡像安全最佳實踐

 

《Docker 鏡像安全最佳實踐速查表[1]》列舉了 10 個訣竅和指南,確保更安全和更高質量的 Docker 鏡像處理。此外,還可以檢視有關 Docker 安全的新報告《Docker 安全要趁早[2]》。

 

 

1. 選用最小化基礎鏡像

 

人們編寫專案的 Dockerfile 時,經常使用一個通用的 Docker 容器鏡像作為基礎,例如 From Node 。 Node 鏡像實際上是以一個完整安裝的 Debian Stretch 發行版為基礎,這意味著構建得到的專案容器鏡像將包含一個完整的操作系統。如果該專案不需要任何通用的系統庫或者系統工具應用,最好不要使用完整的操作系統作為基礎鏡像。
Synx 發佈的《開源安全報告-2019[3]》指出,Docker Hub 上流行的很多容器鏡像,都用到了包含大量已知安全漏洞的基礎鏡像。例如,執行 docker pull node ,下載並使用 Node 鏡像,相當於在應用中引入了一個包含 580 個已知漏洞的操作系統。
從上圖(摘自《開源安全報告-2019》)可知,Docker Hub 上最流行的 10 個鏡像都包含已知的安全漏洞。選用最小化基礎鏡像,即只包含專案確實需要的系統工具和庫的鏡像,就能最小化系統的攻擊面,確保所用操作系統是安全的。
瞭解更多 Docker 鏡像安全的知識[4]。
2. 設定最小權限的 USER

 

如果 Dockerfile 中沒有指定 USER ,Docker 預設將會以超級用戶 root 的身份運行容器,容器所屬的命名空間(namespace)因此映射為 root 所有,這意味著容器有可能獲取 Docker 宿主機的超級管理權限。不僅如此,以 root 用戶身份運行容器,還擴大了攻擊面,如果容器應用中存在安全漏洞,很容易造成權限提升。
在實踐中,一般不需要容器擁有 root 權限。為了儘量降低安全威脅,創建專門的用戶和用戶組,在 Dockerfile 中使用 USER 指定用戶,確保以最小權限的用戶身份運行容器應用。
如果基礎鏡像中不包含專門的用戶,那麼就在 Dockerfile 中直接創建。下麵就是一個這樣的例子,它用到的基礎鏡像是 Ubuntu :

 

  1. FROM ubuntu
  2. RUN mkdir /app
  3. RUN groupadd -r lirantal && useradd -r -s /bin/false -g lirantal lirantal
  4. WORKDIR /app
  5. COPY . /app
  6. RUN chown -R lirantal:lirantal /app
  7. USER lirantal
  8. CMD node index.js
在上例中:
  • 創建一個系統用戶( -r 選項),沒有密碼、沒有主目錄且沒有 shell;

  • 將該用戶添加到前面(使用 groupadd )創建的用戶組;

  • 最後一段引數設定了用戶名以及所屬的用戶組。

 

如果你使用的是 Node.js 和 alpine 鏡像,已經包含了一個用戶 node,直接使用即可:
  1. FROM node:10-alpine
  2. RUN mkdir /app
  3. COPY . /app
  4. RUN chown -R node:node /app
  5. USER node
  6. CMD [“node”, index.js”]
Node.js 應用開發者請參閱官方的Docker 和 Node.js 最佳實踐[5]。
3. 簽名和校驗鏡像,防範中間人攻擊

 

Docker 鏡像的認證頗具挑戰性。在生產環境使用這些鏡像運行我們的代碼,意味著我們對這些鏡像的極大信任。因此,必須保證我們拉取的容器鏡像確實是發佈者發佈的鏡像,沒有被任何人篡改。發生鏡像篡改,有可能是因為 Docker 客戶端和鏡像中心之間的中間人攻擊,或者是發佈者的身份被人盜用併在鏡像中心發佈了惡意鏡像。
校驗 Docker 鏡像
Docker 預設直接拉取容器鏡像,不會校驗鏡像的來源和發佈者。這意味著你有可能使用來源和發佈者不明的任何鏡像。
無論採用何種策略,最佳實踐都是先校驗容器鏡像,通過驗證後再拉取鏡像。為了體驗鏡像校驗功能,執行下列暫時開啟 Docker Content Trust 的命令:
  1. export DOCKER_CONTENT_TRUST=1
現在,嘗試拉取一個沒有簽名的容器鏡像——請求會被拒絕,不會拉取鏡像。
簽名 Docker 鏡像
優先使用 Docker 認證的鏡像,即這些鏡像來自經過 Docker Hub 檢查和選擇的可信提供者。不要使用無法檢驗來源和發佈者的容器鏡像。
Docker 支持鏡像簽名,提供了額外一層的保護。使用 Docker Notary 簽名鏡像。Notary 會檢驗鏡像的簽名,如果簽名不合法,它會阻止運行該鏡像。
如果開啟了 Docker Content Trust ,構建 Docker 鏡像的同時也會對鏡像簽名。如果是第一次簽名,Docker 會為當前用戶生成一個私鑰,儲存在 ~/docker/trust 。後續所有的鏡像都會使用這個私鑰簽名。
請參考Docker 官方文件[6],瞭解簽名鏡像的詳細指令。
4. 找出、修正和監控開源漏洞

 

指定容器的基礎鏡像,同時也引入了該鏡像包含的操作系統及系統庫有可能存在的所有安全風險。
最好選用能夠正常運行應用代碼的最小化鏡像,這有助於減少攻擊面,因為限制了可能的安全漏洞數量。不過,這麼做並沒有對鏡像進行安全審計,也不能防範將來發現的新漏洞。
因此,防範安全軟體漏洞的一種方法是使用像 Snyk 這樣的工具,持續掃描和監控 Docker 鏡像各層可能存在的漏洞。
使用下列命令掃描容器鏡像,檢查是否存在已知漏洞:
  1. # fetch the image to be tested so it exists locally
  2. $ docker pull node:10
  3. # scan the image with snyk
  4. $ snyk test --docker node:10 --file=path/to/Dockerfile
Snyk 能夠監控指定的容器鏡像,一旦有新發現的安全漏洞,通知用戶並給出修補建議:
  1. $ snyk monitor --docker node:10
根據 Snyk 用戶執行的鏡像掃描,我們發現大約 40% 的 Docker 鏡像包含已知漏洞,實際上彌補這些漏洞的新版本基礎鏡像已經有了。 Synx 提供了絕無僅有的修正建議功能,用戶可以根據建議採取行動,升級 Docker 鏡像。
Snyk 還發現在掃描的所有鏡像中,為了減少漏洞的數量,大約 20% 的鏡像需要重新構建。更多信息請參閱《開源安全報告-2019[3]》。
5. 不要在容器鏡像中包含機密信息

 

有時候,構建包含應用的容器鏡像時,需要用到一些機密信息,例如從私有倉庫拉取代碼所需的 SSH 私鑰,或者安全私有軟體包所需的令牌。如果 Dockerfile 中包含複製機密信息的命令,構建鏡像時,這行命令對應的中間容器會被快取,導致機密資料也被快取,有可能造成機密信息泄漏。因此,像令牌和密鑰這樣的機密信息必須儲存在 Dockerfile 之外。
使用多階段構建
利用 Docker 的多階段構建功能,用一個中間鏡像層獲取和管理機密信息,然後清除中間鏡像,這樣在應用鏡像構建階段不涉及敏感資料。如下麵例子所示,使用代碼將機密信息添加到中間層:
  1. FROM: ubuntu as intermediate
  2. WORKDIR /app
  3. COPY secret/key /tmp/
  4. RUN scp -i /tmp/key [email protected]/files .
  5. FROM ubuntu
  6. WORKDIR /app
  7. COPY --from intermediate /app .
使用 Docker 的 secret 管理功能
使用 Docker 的 secret 管理功能(alpha 階段),加載敏感信息檔案且不會快取這些信息:
  1. # syntax = docker/dockerfile:1.0-experimental
  2. FROM alpine
  3. # shows secret from default secret location
  4. RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecre
  5. # shows secret from custom secret location
  6. RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar
想瞭解有關 Docker secret 的更多信息,請訪問 Docker 官方站點[6]。
避免無意中複製機密信息
往鏡像中複製檔案時,也要當心,避免無意中添加了機密信息。例如,下麵的命令將整個構建背景關係檔案夾複製到 Docker 鏡像,有可能把敏感檔案也複製進去了:
  1. COPY . .
如果檔案夾中有敏感檔案,要麼先移除這些檔案,要麼將這些檔案包含在 .dockerignore 中,複製時會忽略這些檔案:
  1. private.key
  2. appsettings.json
6. 設定鏡像的標簽,保證鏡像的不可更改性

 

每個 Docker 鏡像可以有多個標簽(tag),代表該鏡像的不同變體。最常見的標簽是 latest ,表示這是該鏡像的最新版本。鏡像標簽是可更改的,也就是說鏡像的作者可以多次發佈相同標簽的鏡像。
因此,即使你的 Dockerfile 明確指定了使用的基礎鏡像及其標簽,這次鏡像構建和下次鏡像構建仍然可能用到了不同的基礎鏡像。解決這個問題,有多種辦法:
  • 優先選用最詳細的鏡像標簽。例如,鏡像有 :8、:8.0.1 和 :8.0.1-alpine 等標簽,選擇最後這個,因為它提供了最詳細的信息。不要使用像 latest 這樣過於泛泛的標簽。

  • 記住,鏡像的發佈者有可能刪除鏡像的某個標簽。如果設定了所用鏡像的標簽,一旦這個標簽被刪除,鏡像構建會因為找不到基礎鏡像而失敗。為了避免這個問題,可以提前把該鏡像複製到私有鏡像中心或者公有鏡像中心的私人賬戶下麵。這麼做,保證了鏡像的不可更改性,同時也帶來了維護私有鏡像中心的負擔。

  • 使用比簽名更具體的 SHA256 取用指明要使用的鏡像,這能保證每次拉取都是相同內容的鏡像。這麼做也有風險,如果鏡像改變了,以前的 SHA256 取用(散列值)也不存在了。

 

7. 使用 COPY ,不要使用 ADD

 

譯者警告:這部分對 ADD 和 COPY 的描述,與 Docker 官方文件並不吻合,譯者按照自己的理解修改了這部分內容。如果要瞭解作者原意,請閱讀英文原文[7]。
從宿主機複製檔案到容器鏡像中的 Docker 命令有兩個:COPY 和 ADD ,這兩個命令本質上很相似,但具體功能並不相同:
  • COPY – 將本地檔案或者目錄(遞迴)複製到容器鏡像中的標的目錄,複製來源和標的都必須明確指定。

  • ADD – 與 COPY 類似的功能,有兩個不同:(1)如果複製來源是本地壓縮檔案,ADD 將把該檔案解壓縮到標的目錄;(2)ADD 也可以將遠程 URL 指定的檔案下載到標的目錄。

為了避免可能導致的安全問題,請記住 COPY 和 ADD 的不同:
  • 使用 ADD 從遠程 URL 下載檔案,存在中間人攻擊的風險,檔案內容有可能因此被篡改。必須確保遠程 URL 必須是安全的 TLS 鏈接,校驗遠程 URL 的來源和身份。譯者註:實際上,官方文件並不鼓勵使用 ADD 添加遠程檔案。

  • 如果複製的是本地壓縮檔案, ADD 自動將它解壓縮到標的目錄,這有可能觸發 zip 炸彈或者 zip 任意檔案改寫漏洞。

  • 相比較而言,使用 COPY 複製檔案或目錄,會創建一個快取的中間鏡像層,優化鏡像構建的速度。

 

8. 使用 LABEL 指定鏡像元資料

 

鏡像元資料有助於用戶更好地理解和使用該鏡像。最常見的元資料是 maintainer ,它說明瞭鏡像維護者的電郵地址和名字。使用 LABEL 命令添加鏡像的元資料:
  1. LABEL maintainer="[email protected]"
除了鏡像的維護者信息,添加其他你認為重要的元資料,包括提交物件的散列值、相關構建的鏈接、質量狀態(通過所有測試了嗎?)、原始碼鏈接、SECURITY.TXT 檔案的位置等。
SECURITY.TXT (RFC5785)[8] 檔案說明瞭鏡像維護者的安全披露政策。最好在鏡像元資料中加上 SECURITY.TXT 的鏈接,例如:
  1. LABEL securitytxt="https://www.example.com/.well-known/security.txt"
想瞭解鏡像元資料的更多信息,請訪問 https://label-schema.org/rc1/ 。
譯者註:這個規範好像已經廢止了,請直接訪問 OCI 鏡像規範[9]。
9. 使用多階段構建小而安全的鏡像

 

使用 Dockerfile 構建應用容器鏡像時,會生成很多只是構建時需要的鏡像層,包括編譯時所需的開發工具和庫,運行單元測試所需的依賴、臨時檔案、機密信息等等。
如果保留這些鏡像層,不僅會增加鏡像的大小,影響鏡像下載速度,而且會因為安裝更多軟體包而面臨更大的攻擊危險。這對用到的鏡像也是成立的——需要使用一個專門構建應用的鏡像,但不會用它來運行應用代碼。
Go 語言就是一個很好的例子。構建一個 Go 應用需要用到 Go 編譯器。編譯得到的 Go 應用能夠在任何操作系統上直接運行,沒有任何依賴,包括 scratch 鏡像。
Docker 因此提供了多階段構建的功能,允許在構建過程中使用多個臨時鏡像,只保留最後一個鏡像。這樣,用戶得到兩個鏡像:
  • 第一個鏡像——非常大的鏡像,包含了構建應用和運行測試所需的所有依賴;

  • 第二個鏡像——非常小的鏡像,只包含運行應用所需的極少數依賴。

10. 使用靜態分析工具

 

使用靜態分析工具,能夠避免常見的錯誤,建立工程師自動遵循的最佳實踐指南。
例如,hadolint 分析 Dockerfile 併列出不符合最佳實踐規則的地方。
在集成開發環境(IDE)中使用 hadolint 更好。例如,安裝 VS Code 的 hadolint 擴展後,編寫 Dockerfile 時,邊寫邊檢查,既快又好。
瞭解更多 Docker 鏡像安全的知識[10]。
把速查表打印出來,貼在某處,時刻提醒自己構建容器鏡像時應該遵循的最佳安全實踐!
相關鏈接:
  1. https://res.cloudinary.com/snyk/image/upload/v1551798390/Docker_Image_Security_Best_Practices_.pdf

  2. https://snyk.io/blog/shifting-docker-security-left/

  3. https://snyk.io/blog/top-ten-most-popular-docker-images-each-contain-at-least-30-vulnerabilities/

  4. https://snyk.io/container-vulnerability-management/

  5. https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md

  6. https://docs.docker.com/engine/security/trust/content_trust/

  7. https://snyk.io/blog/10-docker-image-security-best-practices/

  8. https://securitytxt.org/

  9. https://github.com/opencontainers/image-spec

  10. https://snyk.io/container-vulnerability-management/

     

原文鏈接:https://snyk.io/blog/10-docker-image-security-best-practices/
赞(0)

分享創造快樂