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映象的時候,實際上有很多選擇。其中就包括了Node.js核心團隊維護的官方Docker映象,以及從特定的基礎映象中選擇的特殊Node.js映象版本。還可以選擇其他的,比如谷歌在distroless
專案上構建的Node.js應用程式,或者是Docker官方團隊提供的一個名為scratch
的映象。
在這些Node.js的Docker映象中,哪一個是最適合你的呢?
讓我們一個一個的去分析,瞭解更多他們的好處和潛在風險。
作者注:在這篇文章中,我將會比較18.2.0這個版本的Node.js映象,他的釋出時間是2022年6月左右。
我們先從維護的node映象開始。它是由進行正式地維護的,包含了一些基礎的映象tag。這些tag對應到不同的底層發行版(Debian、Ubuntu、Alpine)以及不同版本的Node.js執行時本身。還有針對不同CPU架構的特定版本tag,例如amd64
和arm64x8
(新版蘋果的M1)。
Debian發行版中最常見的node
映象,例如bullseye
或buster
,他們都是基於由另一個團隊維護的buildpack-deps
的。
當你基於這個預設的node
映象構建Node.js Docker映象時會發生什麼?
FROM node
登入後複製
使用docker build --no-cache -f Dockerfile1 -t dockerfile1
構建映象時,就會出現下面這些:
node
是node:last
的一個別名,他的版本指向的是18.2.0
這個最新的Node.js映象的依賴和安全漏洞足跡是什麼呢?我們可以用docker scan dockerfile1
來執行一個Synk-powered
容器,就會得到下面的結果:
curl/libcurl4
、git/git-man
、imagemagick/imagemagick-6-common
。Buffer Overflows
、use-after-free errors
、out-of-bounds write
等。DNS Rebinding
、HTTP Request Smuggling
、Configuration Hijacking
譯者注:
- Buffer Overflows - 緩衝區溢位
- Use After Free - 一種記憶體破壞漏洞,通常存在於瀏覽器中
- out-of-bounds write - 越界寫入
- DNS Rebinding - DNS重繫結攻擊
- HTTP Request Smugglin - HTTP請求夾帶攻擊技術
- Configuration Hijacking - 設定劫持
你真的需要在Node.js映象中給你的應用提供wget
、git
、curl
嗎?在Node.js Docker映象中,有成百上千個依賴和工具,而這些依賴又對應著成百上千個漏洞。Node.js執行時的特性對應著7個不同的安全漏洞,給潛在攻擊留下了很大的空間。總的來說,情況並不是很樂觀。
如果你在Node.js Docker Hub倉庫上瀏覽可用tags,你將會發現有兩個Node.js映象tags - node:buster
和node: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
的結果進行比較,將會得到完全相同的大小、依賴數量和發現的漏洞。原因是node
、node:latest
、node:bullseye
全部指向了同一個正在構建的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
登入後複製
就容器映象大小和安全狀況而言,node:bullseye-slim
已經是一個比較好的起點了。
到目前為止,我們的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.js Docker團隊維護了一個node:alpine
映象tag以及他的變體,以便將Alpine Linux發行版的特定版本與Node.js執行時的特定版本進行匹配。
Alpine Linux專案經常因為其非常小的映象體積而被參照,小體積意味著更新的軟體佔用空間和更少的漏洞,確實十分不錯。
下面的命令會讓Dockerfile去生成一個node環境,這個將會增加未壓縮的映象體積:
FROM node:alpine
登入後複製
這個將會產生一個178MB大小的docker映象,和slim
Node.js映象大小差不多,但是在alpine
映象tag中,只檢測到了16個系統依賴漏洞和2個安全安全漏洞。這就意味著alpine
映象tag對於小體積和漏洞數量來說是一個比較好的選擇。
alpine
對Node.js映象可能提供了一個較小的映象體積和更少的漏洞數量。但是,我們必須意識到Alpine專案使用musl
作為C標準庫的實現。而Debian的Node.js映象tag依賴於glibc
實現,比如bullseye
和slim
。這些差異可以解釋效能問題、功能性的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基礎映象有關,但是也應該考慮到這一點。
distroless
(無失真)Docker映象我們的基準測試的最後一個比較專案是谷歌的Distroless容器映象。
distroless
容器映象?這種映象甚至比slim
的Node.js映象更加小,因為distroless
映象只針對這個應用和應用執行時的依賴性而已。因此,一個distroless
的docker映象沒有容器包管理器、shell、或者其他通用工具的依賴性,這使得它們的體積更小,漏洞也更少。
幸運的是,Distroless專案為Node.js維護了一個特殊執行時的distroless
docker映象,通過其完整的名稱空間識別為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"]
登入後複製
構建這個distroless
docker映象將產生112MB的檔案,而在slim
和alpine
映象tag來說,這已經減小了很多檔案的體積了。
如果你正在考慮使用distroless
docker映象,有一些重要的事項需要注意:
glibc
實現,並且不太可能在生產環境出現一些奇怪的問題。nodejs:16
的標記(該標記經常更新),或者在一個特定時間點根據映象的SHA256雜湊值進行安裝。我們可以參考下面的表格來總結不同Node.js Docker映象tags之間的比較:
Image tag | Node.js runtime version | OS dependencies | OS security vulnerabilities | High and Critical vulnerabilities | Medium vulnerabilities | Low vulnerabilities | Node.js runtime vulnerabilities | Image size | Yarn available |
---|---|---|---|---|---|---|---|---|---|
node | 18.2.0 | 409 | 289 | 54 | 18 | 217 | 7 | 952MB | Yes |
node:bullseye | 18.2.0 | 409 | 289 | 54 | 18 | 217 | 7 | 952MB | Yes |
node:bullseye-slim | 18.2.0 | 97 | 56 | 4 | 8 | 44 | 7 | 246MB | Yes |
node:lts-bullseye-slim | 16.15.0 | 97 | 55 | 4 | 7 | 44 | 6 | 188MB | Yes |
node:alpine | 18.2.0 | 16 | 2 | 2 | 0 | 0 | 0 | 178MB | Yes |
gcr.io/distroless/nodejs:16 | 16.17.0 | 9 | 11 | 0 | 0 | 11 | 0 | 112MB | No |
我們來通過之前學習到的每個不同的Node.js映象tags的資料和見解,然後確定哪個是最理想的。
如果你選擇使用的Node.js映象tag取決於開發的一致性(這意味你希望為開發和生產完全相同的環境進行優化),那麼這可能已經是一場失敗的battle了。在大多數情況下,所有3個主要作業系統都使用不同的C庫實現。Linux依賴glibc,Alpine依賴musl,而macOS有自己的BSD libc實現。
有時候,映象大小也很重要。更準確地說,我們的目標不是擁有最小的體積,而是擁有最小的整體軟體佔用。在這種情況下,slim
映象tags和alpine
沒有太大的區別,而且它們對於一個容器映象來說平均大概是200MB。當然,slim
映象的軟體佔用相對於alpine
來說還是相當高的(slim
97個 VS alpine
16個),因此,漏洞數量對於alipne
來說也更高(slime
56個 VS slpine
2個)。
漏洞是一個重要的問題,並且一直是很多文章的中心——關於你為什麼應該減少容器映象的大小。
忽略node
和node:bullseye
這種由於較大的軟體佔用而跟著增加安全漏洞的映象,我們可以更關注稍微小一點的映象型別。在slim
、alpine
、distroless
之間對比,高危和關鍵安全漏洞的絕對數量並不高,範圍在0到4之間,這是一個可控的風險,並不會影響到你的應用程式。
最理想的Node.js Docker映象應該是一個基於現代Debian OS的作業系統的精簡版本,它有一個穩定且活躍的Node.js長期支援版本。
歸根結底就是選擇node:lts-bullseye-slim
Node.js映象tag。我贊成使用確定性影象標記,因此我要做的細微更改是使用實際的底層版本號,而不是lts
別名。
最理想的Node.js Docker映象tag是**node:16.17.0-bullseye-slim**
。
如果你在一個成熟的開發團隊工作,該團隊可以支援自定義基礎映象,那麼我的第二個最佳建議是選擇谷歌的distroless
映象tag,因為它保持了glibc
對官方Node.js執行時版本的相容性。這個工作流會需要一些維護,所以我只是建議而已。
更多node相關知識,請存取:!
以上就是聊聊如何選擇一個最好的Node.js Docker映象?的詳細內容,更多請關注TW511.COM其它相關文章!