自從進入大容器時代後,Docker、K8s 已經逐漸成為開發、測試及部署時不可或缺的工具,但也因為這樣跟容器有關的攻擊越來越普遍
而想要從零開始建出一個容器,第一步就是要寫 Dockerfile 把你的應用包裝成 Docker image。關於怎麼產生出盡量小的 image 已經很多人寫過了,所以這兩天要來說說想要寫出一個安全的 Dockerfile,有哪些該注意的地方
很多人在寫 Dockerfile 並不會特別指定 base image 的版本(就懶啊,我懂 XD),譬如說想要包一個 Node API server,就直接寫 FROM node
或是 FROM node:latest
# Bad
FROM node
WORKDIR /app
COPY . .
RUN npm install
RUN npm run test
但這樣可能會在哪次 build image 時就意外從 Node 14 升到 Node 16,導致部分功能直接壞掉。而且最新版本的 Node 可能有一些不為人知的 bug,需要有一些勇者去幫忙踩坑,所以除非是自己的 Side Project 想要玩玩看最新的 feature,否則直接把最新版本的 Node 用在 production 並不是個好作法
比較好的方式是先看看 Node 的 LTS(Long-Term Support) 版本是多少,像我在寫文章的當下是 v14.17.5
,那就選擇 node:14
或是 node:14.17
作為 base image
# Good
FROM node:14
WORKDIR /app
COPY . .
RUN npm install
RUN npm run test
這點跟上面提到的不要用 latest image 有些類似,不管你是用 apt-get install
安裝 CLI 工具、用 npm install
裝函式庫、還是用 curl/wget 把東西下載回來編譯,都要盡量確保每次下載到的東西是一樣的
譬如在用 apt-get 安裝 nginx 時就可以透過 apt-get install nginx=1.14.0
來下載指定版本(有點麻煩對吧,我也覺得XD),而 npm、pip 這類的語言套件管理工具則是看官方推薦什麼方法,像 npm 就是用 package-lock.json
來鎖定套件的版本、pip 的話則是先跑 pip freeze > requirements.txt
把套件的版本凍起來,等要安裝時再跑 pip install -r requirements.txt
把原本的套件裝回來
# Good
FROM ubuntu:20.04
RUN apt-get update
RUN apt-get install nginx=1.14.0 python=2.7.15 nodejs=12.18.2
雖然把套件版本的鎖定之後可以省下很多麻煩,但也不能一直鎖在那都不更新,所以記得偶爾去檢查一下版本是不是太舊了,如果太舊再手動把版號升上去就好了~
我想這已經是常識等級的安全知識了,因為直接把敏感資料用 ENV 寫在 Dockerfile 裡會讓駭客輕易拿到(只要拿到 image 就可以了),所以絕對不要想不開把資料庫或任何的帳號密碼寫在裡面,ENV
頂多用來設定時區或是 NODE_ENV
這種被看光也不會出事的變數就好,不然哪天資料被偷走真的會哭出來
FROM node:14
ENV TZ=Asia/Taipei
ENV NODE_ENV=production
# Very Bad
ENV PG_HOST=test.postgresql.com
ENV PG_USER=thisIsmyUserName
ENV PG_PASS=mySecretPa55w0rd
ENV PG_DBNAME=projectName
COPY . .
RUN npm install
CMD ["node", "index.js"]
如果說 ENV 不能放敏感資料,那這些資料究竟要怎麼被加進環境變數呢?
答案就是在 docker run
時加上 --env
或是 --env-file
把環境變數塞進去;如果是用 docker-compose 的話,則是把那些資料寫進 docker-compose.yml
的 environment 裡面,這樣 container 啟動時就會讀到這些變數,而且即便 image 被偷走也不用擔心資料外洩
今天簡單介紹了三個寫 Dockerfile 時該注意的地方,但因為除了這幾個之外其實還有很多,所以明天會再補充幾個
如果對於今天的內容有什麼問題都歡迎在下方劉言提問,沒問題的話那就明天見囉~