聊聊如何選擇一個最好的Node.js Docker映象?

2022-12-13 22:00:44
選擇一個的Docker映象看起來像是一件小事,但是映象的大小和潛在漏洞可能會對你的CI/CD流程和安全造成重大的影響。那我們如何選擇一個最好Node.js Docker映象呢?

node.js極速入門課程:進入學習

我們在使用FROM node:latest或只是FROM node時,很容易忽略他潛在的風險。如果你不知道總體的安全風險並且把他引入到了CI/CD的流程中,那無疑是加劇了這個風險。【相關教學推薦:、】

下面這個例子非常典型,你可以從很多教學或者部落格文章中看到這個Node.js Dockerfile的設定。但是這個Dockerfile的設定存在很大的問題,非常不推薦這樣使用:

FROM node
WORKDIR /usr/src/app
COPY . /usr/src/app
RUN npm install
CMD "npm" "start"
登入後複製

譯者注:如果需要跳過分析直接看結論,請直接滑到文末。

一個可供選擇Node.js Docker映象

當你構建一個Node.js映象的時候,實際上有很多選擇。其中就包括了Node.js核心團隊維護的官方Docker映象,以及從特定的基礎映象中選擇的特殊Node.js映象版本。還可以選擇其他的,比如谷歌在distroless專案上構建的Node.js應用程式,或者是Docker官方團隊提供的一個名為scratch的映象。

在這些Node.js的Docker映象中,哪一個是最適合你的呢?

讓我們一個一個的去分析,瞭解更多他們的好處和潛在風險。

作者注:在這篇文章中,我將會比較18.2.0這個版本的Node.js映象,他的釋出時間是2022年6月左右。

預設的Node.js映象

我們先從維護的node映象開始。它是由進行正式地維護的,包含了一些基礎的映象tag。這些tag對應到不同的底層發行版(Debian、Ubuntu、Alpine)以及不同版本的Node.js執行時本身。還有針對不同CPU架構的特定版本tag,例如amd64arm64x8(新版蘋果的M1)。

Debian發行版中最常見的node映象,例如bullseyebuster,他們都是基於由另一個團隊維護的buildpack-deps的。
當你基於這個預設的node映象構建Node.js Docker映象時會發生什麼?

FROM node
登入後複製

使用docker build --no-cache -f Dockerfile1 -t dockerfile1構建映象時,就會出現下面這些:

  • 我們沒有指定Node.js執行時版本,所以nodenode:last的一個別名,他的版本指向的是18.2.0
  • 這個Node.js Docker映象大小是952MB

這個最新的Node.js映象的依賴和安全漏洞足跡是什麼呢?我們可以用docker scan dockerfile1來執行一個Synk-powered容器,就會得到下面的結果:

  • 共有409個依賴項 - 這些是使用作業系統包管理器檢測到的開源庫,比如curl/libcurl4git/git-manimagemagick/imagemagick-6-common
  • 共有289個安全問題在這些依賴中被發現,例如,Buffer Overflowsuse-after-free errorsout-of-bounds write等。
  • Node.js 18.2.0執行時版本容易出現7個安全問題。例如,DNS RebindingHTTP Request SmugglingConfiguration Hijacking

譯者注:

  • Buffer Overflows - 緩衝區溢位
  • Use After Free - 一種記憶體破壞漏洞,通常存在於瀏覽器中
  • out-of-bounds write - 越界寫入
  • DNS Rebinding - DNS重繫結攻擊
  • HTTP Request Smugglin - HTTP請求夾帶攻擊技術
  • Configuration Hijacking - 設定劫持

你真的需要在Node.js映象中給你的應用提供wgetgitcurl嗎?在Node.js Docker映象中,有成百上千個依賴和工具,而這些依賴又對應著成百上千個漏洞。Node.js執行時的特性對應著7個不同的安全漏洞,給潛在攻擊留下了很大的空間。總的來說,情況並不是很樂觀。

Node.js Docker Hub選項node:buster vs node:bullseye

如果你在Node.js Docker Hub倉庫上瀏覽可用tags,你將會發現有兩個Node.js映象tags - node:busternode:bullseye

這兩個Docker映象tags都基於Debian發行版。buster映象tag對應著Debian10,將會在2022年8月到2024年進入到他的End of Life日期,所以buster不是一個很好的選擇。bullseye映象tag對應著Debian11,被當做Debian的當前穩定版本,預計EOL日期為2026年6月。

譯者注:

  • End of Life。特指產品壽命的結束,通常縮寫為EOL。

因此,十分建議你將所有新的和現有的Node.js Docker映象從node:buster遷移到node:bullseye或者其他合適的可替代版本。
我們先構建一個新的Node.js Docker映象基於:

FROM node:bullseye複製程式碼
登入後複製

如果你構建了這個Node.js Docker映象tag並且與之前使用node:latest的結果進行比較,將會得到完全相同的大小、依賴數量和發現的漏洞。原因是nodenode:latestnode:bullseye全部指向了同一個正在構建的Node.js映象tag。

Node.js映象tag瘦身

官方的Node.js團隊還維護了一個顯式地針對功能性Node.js環境所需工具的映象tag並且不會存在其他的東西。

這個Node.js映象tags是通過slim映象tag變數來參照的,比如node:bullseye-slim,或者帶有Node.js指定版本,像node:14.19.2 -slim

我們再來基於Debian的當前穩定版本的bullseye構建一個Node.jsslim映象:

FROM node:bullseye-slim
登入後複製
  • 映象的大小已經急劇下降,從接近1GB的容器映象降到246MB的映象大小
  • 掃描他的內容也顯示了整體軟體足跡的大幅下降,只有97個依賴項和56個漏洞。

就容器映象大小和安全狀況而言,node:bullseye-slim已經是一個比較好的起點了。

一個LTS的Node.js Docker映象

到目前為止,我們的Node.js Docker映象基於當前版本的Node.js,即Node.js18。但是根據Node.js的釋出時間表,這個版本直到2022年10月才進入正式的Active LTS狀態。

譯者注:LTS - Long-term support,即長期支援版本。

如果我們總是依賴於我們正在構建的Node.js Docker映象中的LTS版本的話會怎麼樣?我們先更新這個Docker映象tag並構建一個新的Node.js映象:

FROM node:lts-bullseye-slim
登入後複製

瘦身後的Node.js LTS版本(16.15.0)在映象上帶來了相似數量的依賴、安全漏洞和一個略小的體積(188MB)。

因此,儘管你可能需要在LTS和當前Node.js執行時版本中選擇,但他們都不會對Node.js映象的軟體佔用空間有大的影響。

node:alpine對於Node.js映象來說是一個更好的選擇嗎?

Node.js Docker團隊維護了一個node:alpine映象tag以及他的變體,以便將Alpine Linux發行版的特定版本與Node.js執行時的特定版本進行匹配。

Alpine Linux專案經常因為其非常小的映象體積而被參照,小體積意味著更新的軟體佔用空間和更少的漏洞,確實十分不錯。
下面的命令會讓Dockerfile去生成一個node環境,這個將會增加未壓縮的映象體積:

FROM node:alpine
登入後複製

這個將會產生一個178MB大小的docker映象,和slimNode.js映象大小差不多,但是在alpine映象tag中,只檢測到了16個系統依賴漏洞和2個安全安全漏洞。這就意味著alpine映象tag對於小體積和漏洞數量來說是一個比較好的選擇。

alpine對Node.js映象可能提供了一個較小的映象體積和更少的漏洞數量。但是,我們必須意識到Alpine專案使用musl作為C標準庫的實現。而Debian的Node.js映象tag依賴於glibc實現,比如bullseyeslim。這些差異可以解釋效能問題、功能性的bug或者是潛在的應用程式崩潰,這些都是由於底層C庫的差異造成的。

選擇一個alpine的Node.js映象tag意味著你實際上是在選擇一個非官方的Node.js執行時。Node.js Docker團隊並不會正式支援基於alpine的容器映象構建。因此,他宣告基於Alpine的映象tag是實驗性的,並且可能和官方的構建不一致。

如果你正在選一個一個基於Alpine的Node.js Docker映象,需要記住一點,Docker安全工具(例如Trivy或Snyk)目前無法檢測到Alpine映象中與執行時相關的漏洞的。雖然這種情況未來可能會改變,但是目前還不能找到Node.js18.2.0alpine基礎映象tag的安全漏洞,而18.2.0執行時本身實際上是容易受到攻擊的。這與安全工具本身有關,而不是與Alpine基礎映象有關,但是也應該考慮到這一點。

Node.js的distroless(無失真)Docker映象

我們的基準測試的最後一個比較專案是谷歌的Distroless容器映象。

什麼是distroless容器映象?

這種映象甚至比slim的Node.js映象更加小,因為distroless映象只針對這個應用和應用執行時的依賴性而已。因此,一個distroless的docker映象沒有容器包管理器、shell、或者其他通用工具的依賴性,這使得它們的體積更小,漏洞也更少。

幸運的是,Distroless專案為Node.js維護了一個特殊執行時的distrolessdocker映象,通過其完整的名稱空間識別為grc.io/distroless/nodejs-debian11,並且可以在谷歌的容器登入檔中找到(這個是gcr.io的部分)。

因為Distroless容器映象沒有軟體,我們可以使用一個docker的多階段工作流來為我們的容器安裝依賴項,並且把它們複製到Distroless映象:

FROM node:16-bullseye-slim AS build
WORKDIR /usr/src/app
COPY . /usr/src/app
RUN npm install

FROM gcr.io/distroless/nodejs:16
COPY --from=build /usr/src/app /usr/src/app
WORKDIR /usr/src/app
CMD ["server.js"]
登入後複製

構建這個distrolessdocker映象將產生112MB的檔案,而在slimalpine映象tag來說,這已經減小了很多檔案的體積了。
如果你正在考慮使用distrolessdocker映象,有一些重要的事項需要注意:

  • 好的一點是,它們是基於當前穩定的Debian發行版本,這意味著它們是最新的,很久才會到EOL的日期。
  • 因為它們是基於Debian的,所以它們依賴glibc實現,並且不太可能在生產環境出現一些奇怪的問題。
  • 你很快就會發現Distroless團隊沒有維護細粒度的Node.js執行時版本。這意味著你需要依賴於通用的nodejs:16的標記(該標記經常更新),或者在一個特定時間點根據映象的SHA256雜湊值進行安裝。

Node.js Docker映象tags的比較

我們可以參考下面的表格來總結不同Node.js Docker映象tags之間的比較:

Image tagNode.js runtime versionOS dependenciesOS security vulnerabilitiesHigh and Critical vulnerabilitiesMedium vulnerabilitiesLow vulnerabilitiesNode.js runtime vulnerabilitiesImage sizeYarn available
node18.2.040928954182177952MBYes
node:bullseye18.2.040928954182177952MBYes
node:bullseye-slim18.2.0975648447246MBYes
node:lts-bullseye-slim16.15.0975547446188MBYes
node:alpine18.2.01622000178MBYes
gcr.io/distroless/nodejs:1616.17.091100110112MBNo

我們來通過之前學習到的每個不同的Node.js映象tags的資料和見解,然後確定哪個是最理想的。

DEVELOPMENT-PARITY(開發環境平價)

如果你選擇使用的Node.js映象tag取決於開發的一致性(這意味你希望為開發和生產完全相同的環境進行優化),那麼這可能已經是一場失敗的battle了。在大多數情況下,所有3個主要作業系統都使用不同的C庫實現。Linux依賴glibc,Alpine依賴musl,而macOS有自己的BSD libc實現。

DOCKER映象大小

有時候,映象大小也很重要。更準確地說,我們的目標不是擁有最小的體積,而是擁有最小的整體軟體佔用。在這種情況下,slim映象tags和alpine沒有太大的區別,而且它們對於一個容器映象來說平均大概是200MB。當然,slim映象的軟體佔用相對於alpine來說還是相當高的(slim97個 VS alpine16個),因此,漏洞數量對於alipne來說也更高(slime56個 VS slpine2個)。

安全漏洞

漏洞是一個重要的問題,並且一直是很多文章的中心——關於你為什麼應該減少容器映象的大小。

忽略nodenode:bullseye這種由於較大的軟體佔用而跟著增加安全漏洞的映象,我們可以更關注稍微小一點的映象型別。在slimalpinedistroless之間對比,高危和關鍵安全漏洞的絕對數量並不高,範圍在0到4之間,這是一個可控的風險,並不會影響到你的應用程式。

本質內容?

最理想的Node.js Docker映象應該是一個基於現代Debian OS的作業系統的精簡版本,它有一個穩定且活躍的Node.js長期支援版本。

歸根結底就是選擇node:lts-bullseye-slimNode.js映象tag。我贊成使用確定性影象標記,因此我要做的細微更改是使用實際的底層版本號,而不是lts別名。

最理想的Node.js Docker映象tag是**node:16.17.0-bullseye-slim**

如果你在一個成熟的開發團隊工作,該團隊可以支援自定義基礎映象,那麼我的第二個最佳建議是選擇谷歌的distroless映象tag,因為它保持了glibc對官方Node.js執行時版本的相容性。這個工作流會需要一些維護,所以我只是建議而已。

更多node相關知識,請存取:!

以上就是聊聊如何選擇一個最好的Node.js Docker映象?的詳細內容,更多請關注TW511.COM其它相關文章!