容器安全的核心思維
在容器化應用程式持續普及的今日,安全性已成為組織採用Docker的關鍵考量。假設identidock服務具有一定價值並擁有使用者基礎,我們需要建立完善的安全防護措施。當使用者依賴此服務時,確保其持續可靠運作變得尤為重要。
容器安全不是單一措施就能解決的問題,而是需要實施多層防護策略。從映像檔建構到執行時保護,每個環節都需要審慎考量。在玄貓多年的容器安全實踐中發現,最有效的防護策略是深度防禦原則,建立多個安全檢查點,確保即使某一層被突破,其他層仍能提供保護。
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
package "容器安全架構" {
[映像檔安全] as Image
[執行時隔離] as Runtime
[網路安全] as Network
[資料保護] as Data
[存取控制] as Access
}
[Image] --> [Runtime]
[Runtime] --> [Network]
[Runtime] --> [Data]
[Access] --> [Image]
[Access] --> [Runtime]
note right of Image
來源驗證
漏洞掃描
精簡映像
end note
note right of Runtime
資源限制
能力控制
安全模組
end note
@enduml基礎安全措施實施清單
在開始任何容器佈署前,應該確保這些基本安全措施到位。這些措施構成了容器安全的基礎,是保護系統免受常見攻擊的第一道防線。
環境隔離是首要考量。將identidock容器執行在獨立虛擬機器或專用主機上,防止其他服務遭受潛在攻擊的連帶影響。這種物理或邏輯隔離能夠限制攻擊者的橫向移動能力,即使某個容器被入侵,攻擊者也無法輕易存取其他系統。
最小化暴露面同樣重要。僅允許負載平衡器或反向代理容器對外開放連線埠,大幅減少攻擊面。確保監控或日誌服務僅透過私有介面或虛擬私人網路存取,避免不必要的服務暴露在公共網路上。
非root使用者執行是基本但關鍵的安全實踐。為所有identidock映像檔定義非特權使用者,避免以root身份執行容器。即使容器被入侵,攻擊者也無法獲得主機的完整控制權。
映像檔來源驗證確保軟體供應鏈安全。透過雜湊值下載或其他安全驗證方式取得所有映像檔,防止供應鏈攻擊。定期掃描映像檔漏洞,確保使用的基礎映像與依賴套件都是最新且安全的版本。
監控與警示系統能夠及早發現異常行為。佈署監控系統偵測異常流量或行為模式,建立自動化警示機制。這種主動監控能夠在攻擊造成實質損害前發現並阻止威脅。
軟體更新與保護模式確保系統執行在安全狀態。確保所有容器執行最新軟體版本,處於生產模式並關閉所有除錯資訊。除錯資訊可能洩露系統內部結構,成為攻擊者的資訊來源。
強化主機安全提供額外保護層。在主機上啟用AppArmor或SELinux,為容器提供強制存取控制。這些Linux安全模組能夠限制容器的系統呼叫與資源存取,即使容器被入侵也能限制損害範圍。
加強Redis存取控制保護資料儲存。為Redis新增存取控制或密碼保護,防止未授權存取。資料儲存通常是攻擊者的主要目標,必須實施適當的存取控制措施。
進階安全強化措施
在有充足時間與資源的情況下,可以進一步實施這些強化措施,提升系統整體安全水平。
移除不必要的setuid與setgid二進位檔案能夠降低許可權提升風險。從映像檔中移除不必要的特殊許可權二進位檔案,限制攻擊者提升許可權的途徑。大多數應用程式並不需要這些特殊許可權,移除它們不會影響正常功能。
唯讀檔案系統提供額外保護。盡可能以唯讀方式執行檔案系統,dnmonster、identidock容器可以使用唯讀容器檔案系統。Redis容器需要可寫入的掛載卷,但容器本身的檔案系統仍可設為唯讀。這種設定防止攻擊者在容器內植入惡意程式碼或修改設定檔案。
限制核心許可權透過移除不必要的Linux capabilities實作。讓dnmonster與identidock容器在丟棄所有功能的情況下執行,只保留應用程式必要的最小許可權集。這種最小許可權原則大幅降低了容器被利用的風險。
高安全需求場景的特殊措施
對於安全敏感度更高的服務,需要考慮這些更嚴格的措施,提供企業級的安全保障。
記憶體限制防止資源耗盡攻擊。使用記憶體限制引數限制每個容器的記憶體使用,防止某些拒絕服務攻擊與記憶體洩漏。沒有限制的容器可能耗盡主機記憶體,影響其他容器的正常運作。
SELinux專用類別提供精細的存取控制。為容器執行帶有專用類別的SELinux,雖然設定工作量大,但在使用devicemapper儲存驅動時非常有效。SELinux的多類別安全機制能夠將容器嚴格隔離,防止跨容器存取。
程式數量限制防止fork bomb攻擊。對程式數量設定ulimit,防止惡意程式透過大量fork操作耗盡系統資源。這種簡單的限制能夠有效防止某些類別的拒絕服務攻擊。
內部通訊加密增加攻擊難度。加密內部通訊,增加攻擊者篡改資料的難度。即使在內部網路環境,加密通訊也能提供額外的安全保障,特別是在多租戶環境中。
容器隔離策略
按主機隔離容器
在多租戶環境中,無論是組織內部使用者還是外部客戶,將每個使用者的容器放在獨立的Docker主機上至關重要。這種做法雖然比在使用者間分享主機效率低,會導致更多的虛擬機器或實體機器,但對安全性至關重要。
主要原因是防止容器突破隔離後的橫向移動。若發生容器突破,攻擊者取得其他使用者的容器或資料存取權將造成嚴重後果。在獨立虛擬機器或機器環境中,攻擊者仍被限制在隔離的環境,無法輕易存取屬於其他使用者的容器。
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
package "租戶A主機" {
[容器A1]
[容器A2]
[容器A3]
}
package "租戶B主機" {
[容器B1]
[容器B2]
[容器B3]
}
package "敏感資料主機" {
[支付處理容器]
[資料函式庫容器]
}
note right of "租戶A主機"
完全隔離
獨立資源
end note
note right of "敏感資料主機"
額外保護
嚴格存取控制
end note
@enduml處理或儲存敏感資訊的容器應與處理較不敏感資訊的容器分開。特別是要遠離直接暴露給終端使用者的應用程式容器。例如,處理信用卡詳細資料的容器應與執行Node.js前端的容器分開佈署,避免前端漏洞直接威脅到敏感資料。
隔離和使用虛擬機器還能提供對抗拒絕服務攻擊的額外保護。如果使用者被限制在自己的虛擬機器中,他們就無法壟斷主機記憶體並導致其他使用者的服務資源匱乏。這種物理隔離在多租戶環境中特別重要。
在短期到中期內,絕大多數容器佈署將涉及虛擬機器。雖然這不是理想情況,但它確實意味著我們可以結合容器的效率與虛擬機器的安全性,在不犧牲太多效能的情況下提供強大的隔離保障。
應用更新策略
快速對執行系統應用更新對維護安全性至關重要,尤其是當常用工具與框架出現漏洞時。容器技術提供了比傳統虛擬機器更靈活的更新機制,但仍需要謹慎規劃與執行。
容器更新流程
容器化系統的更新過程包含幾個關鍵步驟,每個步驟都需要適當的自動化與驗證機制。
識別需要更新的映像檔是第一步。包括基礎映像檔與所有依賴映像檔,可使用CLI取得執行容器的映像檔列表。建立完整的映像檔依賴關係圖能夠協助我們理解更新的影響範圍。
取得或建立基礎映像檔的更新版本需要確保來源可靠。將此版本推播到登入檔或下載站點,確保所有環境都能取得最新版本。使用自動化構建系統能夠加速這個過程。
重建依賴映像檔時使用no-cache引數確保獲得最新依賴。對每個依賴映像檔執行docker build,並推播這些映像檔到登入檔。這個過程應該自動化,減少人為錯誤。
更新主機映像檔透過在每個Docker主機上執行docker pull完成。確保取得最新映像檔,避免使用過期版本。可以使用配置管理工具如Ansible或Chef自動化這個過程。
重啟容器需要適當的策略避免服務中斷。在每個Docker主機上重啟容器,使用滾動更新或藍綠佈署策略確保服務可用性。監控更新過程,確保新版本正常運作。
清理舊映像檔回收儲存空間。確認一切正常運作後,從主機移除舊映像檔。如果可能,也從登入檔中移除舊版本,但保留適當的版本歷史以便回滾。
取得執行映像檔列表
Docker提供多種方式取得容器與映像檔的資訊。以下命令展示如何取得所有執行容器的映像檔ID:
docker inspect -f "{{.Image}}" $(docker ps -q)
這個命令會輸出所有執行中容器使用的映像檔ID。我們可以使用更多shell技巧取得更詳細的資訊:
docker images --no-trunc | \
grep $(docker inspect -f "-e {{.Image}}" $(docker ps -q))
要取得所有映像檔及其基礎或中間映像檔的列表:
docker inspect -f "{{.Image}}" $(docker ps -q) | \
xargs -L 1 docker history -q
進一步擴充套件以取得映像檔詳細資訊:
docker images | \
grep $(docker inspect -f "{{.Image}}" $(docker ps -q) | \
xargs -L 1 docker history -q | sed "s/^/-e /")
這些命令能夠協助我們建立完整的映像檔清單,確保更新過程不遺漏任何依賴。
自動化更新機制
Docker Hub提供自動構建功能,當基礎映像檔變更時自動觸發重建。透過設定儲存函式庫與基礎映像檔的連結,當基礎映像檔更新時,相依映像檔將自動重建。這種自動化機制大幅減少了人工維護負擔。
儲存驅動演進與安全考量
Docker正從AUFS轉向Overlay作為偏好的儲存驅動。這項變革有其必要性,主要因為AUFS驅動已經從Linux核心中移除且不再積極開發。所有AUFS使用者應儘快遷移至Overlay驅動,確保獲得持續的安全更新與效能改進。
儲存驅動的選擇直接影響容器的穩定性與安全性。不同驅動在效能、可靠性與安全特性上存在差異,選擇適合工作負載的驅動至關重要。
映像檔來源安全保障
確保映像檔的來源安全是容器安全的基礎。映像檔來源驗證涉及兩個核心問題:映像檔從何處來,以及誰建立了它。
安全雜湊基本概念
映像檔來源驗證的基礎是安全雜湊。安全雜湊就像是資料的指紋,是一個相對較小的字串,對特定資料具有唯一性。任何對原始資料的修改都會導致雜湊值發生變化。
常見的安全雜湊演算法包括SHA家族與MD5。SHA-256是目前廣泛使用的安全雜湊演算法,提供良好的安全性與效能平衡。MD5存在根本性安全問題,應避免在安全關鍵場景使用。
當我們擁有資料的安全雜湊值與資料本身時,可以重新計算資料的雜湊值並進行比對。如果雜湊值符合,我們可以確信資料沒有被破壞或篡改。這種驗證機制簡單但有效。
加密簽名與發行者身份驗證
透過加密簽名,我們可以驗證映像檔發行者的身份。當發行者使用其私鑰簽署映像檔時,任何接收方都可以使用發行者的公鑰來驗證簽名,從而確認映像檔確實來自該發行者且未經篡改。
這種機制的前提是客戶端已取得發行者的公鑰,且該公鑰未被洩露。在實際部署中,需要建立可信的公鑰分發機制,確保客戶端獲得的公鑰確實屬於預期的發行者。
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
actor "映像發行者" as Publisher
participant "私鑰" as PrivateKey
participant "映像檔" as Image
participant "簽名" as Signature
actor "映像使用者" as User
participant "公鑰" as PublicKey
Publisher -> PrivateKey : 使用私鑰
PrivateKey -> Image : 簽署映像
Image -> Signature : 生成簽名
Signature -> User : 分發映像與簽名
User -> PublicKey : 使用公鑰
PublicKey -> Signature : 驗證簽名
Signature --> User : 確認完整性與來源
@endumlDocker摘要機制
在Docker術語中,安全雜湊被稱為摘要(Digest)。摘要是檔案系統層或清單的SHA256雜湊值。清單是描述Docker映像檔組成部分的元資料檔案。
由於清單包含了所有映像檔層的摘要列表,如果能夠驗證清單本身未被篡改,就可以安全地下載並信任所有層,即使是透過不受信任的通道如HTTP。這種雜湊鏈結構提供了端到端的完整性保證。
這種設計在許多分散式系統中都有應用,如BitTorrent與區塊鏈網路,被稱為雜湊列表。透過驗證頂層雜湊,可以確保整個資料結構的完整性。
Docker Content Trust機制
Docker在1.8版本引入了Content Trust機制。這是Docker允許發行者簽署其內容的機制,完成了受信任分發架構。當使用者從儲存函式庫提取映像檔時,會收到包含發行者公鑰的證書,允許驗證映像檔確實來自該發行者。
啟用與使用Content Trust
啟用Content Trust機制透過設定環境變數實作:
export DOCKER_CONTENT_TRUST=1
啟用後,Docker引擎將只操作已簽署的映像檔,並拒絕執行任何簽名或摘要不符合的映像檔。這提供了強大的安全保障,確保只有經過授權的映像檔能夠執行。
嘗試提取已簽署與未簽署的映像檔會有不同結果:
docker pull debian:wheezy
docker pull amouat/identidock:unsigned
官方簽署的Debian映像檔會成功提取,而未簽署的映像檔會被拒絕。這種機制有效防止了未授權映像檔的執行。
推播簽署映像檔
推播簽署的映像檔過程簡單:
docker push amouat/identidock:newest
首次推播到儲存函式庫時,Docker會建立根簽名金鑰與標籤金鑰。根金鑰需要妥善保管,如果丟失將導致嚴重問題。沒有手動移除舊證書,儲存函式庫的使用者將無法提取新映像檔或更新現有映像檔。
金鑰備份與管理
金鑰管理是Content Trust機制的關鍵環節。Docker會加密所有靜態金鑰,確保私人材料永遠不會以明文寫入磁碟。由於金鑰的重要性,建議將它們備份在兩個加密USB儲存裝置上,並存放在安全的位置。
建立包含金鑰的TAR檔案:
umask 077
tar -zcvf private_keys_backup.tar.gz ~/.docker/trust/private
umask 022
umask命令確保檔案許可權設定為只讀。根金鑰只在建立或復原金鑰時需要,不使用時應該離線儲存在安全位置。
標籤金鑰是為發行者擁有的每個儲存函式庫建立的。標籤金鑰由根金鑰簽名,允許擁有發行者證書的任何使用者對其進行驗證。標籤金鑰可以在組織內分享,用於簽署該儲存函式庫的任何映像檔。
如果標籤金鑰被洩露,仍然可以還原。透過輪換標籤金鑰,可以從系統中移除被洩露的金鑰。此過程對使用者是透明的,可以主動進行以防止未檢測到的金鑰洩露。
新鮮度保證與重放攻擊防護
Content Trust提供新鮮度保證,防止重放攻擊。重放攻擊發生在當一個物件被替換為以前有效的物件時。例如,攻擊者可能用較舊的已知有漏洞的版本替換二進位檔案。
為避免這種情況,Content Trust利用與每個儲存函式庫關聯的時間戳金鑰。這些金鑰用於簽署儲存函式庫的元資料。元資料具有較短的到期日,需要由時間戳金鑰頻繁重新簽署。
透過在下載映像檔之前驗證元資料未過期,Docker客戶端可以確保接收到最新的映像檔。時間戳金鑰由Docker Hub管理,不需要發行者的任何互動。
處理未簽署映像檔
一個儲存函式庫可以同時包含已簽署與未簽署的映像檔。如果啟用了Content Trust但想要下載未簽署的映像檔,使用disable-content-trust標誌:
docker pull --disable-content-trust amouat/identidock:unsigned
這種彈性允許在遷移期間同時支援已簽署與未簽署的映像檔,但在生產環境中應該要求所有映像檔都經過簽署。
可重現與可信任的Dockerfile
理想情況下,Dockerfile應該每次都產生完全相同的映像檔。但在實踐中這很難實作,同一個Dockerfile很可能隨著時間產生不同的映像檔。透過遵循以下規則,可以接近完全可重現的構建。
FROM指令中指定標籤
# 不佳做法,latest標籤會隨時間變化
FROM redis
# 較好做法,但仍可能隨小版本更新變化
FROM redis:3.0
# 最佳做法,保證每次提取完全相同的映像檔
FROM redis@sha256:3479bbcab384fa343b52743b933661335448f8166203688006...
使用摘要還可以防止意外損壞或篡改,提供額外的安全保障。
安裝軟體時提供版本號
# 可以接受,但不夠精確
apt-get install cowsay
# 更好的做法,明確指定版本
apt-get install cowsay=3.03+dfsg1-6
這適用於其他套件安裝工具如pip。如果可以的話提供版本號,如果舊套件被移除,構建將失敗,但至少這會給你警告。
驗證下載的軟體或資料
這是列出的所有步驟中最重要的。如果不驗證下載,可能會受到意外損壞與攻擊者篡改下載的影響。當軟體透過HTTP傳輸時尤其重要,因為HTTP不提供防止中間人攻擊的保證。
大多數廠商會提供簽名的校驗和用於驗證下載。以下是Node.js映像檔的範例:
RUN gpg --keyserver pool.sks-keyservers.net \
--recv-keys 7937DFD2AB06298B2293C3187D33FF9D0246406D \
114F43EE0176B71C7BC219DD50A3051F888C628D
ENV NODE_VERSION 0.10.38
ENV NPM_VERSION 2.10.0
RUN curl -SLO "http://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" \
&& curl -SLO "http://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --verify SHASUMS256.txt.asc \
&& grep " node-v$NODE_VERSION-linux-x64.tar.gz\$" SHASUMS256.txt.asc | sha256sum -c -
這個流程取得GPG金鑰,下載Node.js壓縮檔與校驗和,使用GPG驗證校驗和簽名,最後檢查校驗和是否與壓縮檔符合。
容器安全技巧
設定非特權使用者
永遠不要在容器內以root身份執行生產應用程式。這值得強調:永遠不要在容器內以root身份執行生產應用程式。攻擊者如果破壞應用程式,將完全存取容器,包括其資料與程式。
Dockerfile應該建立非特權使用者並使用USER陳述式切換:
RUN groupadd -r user_grp && useradd -r -g user_grp user
USER user
這建立了新使用者與群組,USER陳述式將對所有後續指令生效,並在從映像啟動容器時生效。
使用gosu取代sudo
在容器入口指令碼中,gosu是比sudo更好的選擇。使用sudo會產生額外的程式,與訊號處理問題。gosu確保命令以PID 1執行,能正確接收傳送給容器的訊號。
#!/bin/bash
set -e
if [ "$1" = 'redis-server' ]; then
chown -R redis .
exec gosu redis "$@"
fi
exec "$@"
這個入口點指令碼使用gosu切換到redis使用者,確保Redis伺服器正確執行。
限制容器網路通訊
容器應該只開放生產環境中需要的連線埠,且這些連線埠應該只對需要它們的其他容器開放。預設情況下容器之間可以相互通訊,無論是否明確發布或暴露了連線埠。
停用容器間通訊透過設定Docker守護程式的icc標誌:
# 設定Docker守護程式
DOCKER_OPTS="--iptables=true --icc=false"
這將關閉容器間通訊,任何明確連線的容器仍然可以通訊。這種設定防止被攻擊的容器能夠連線到其他容器。
發布連線埠時指定特定介面:
docker run -p 127.0.0.1:8080:8080 -d myimage
這透過只允許來自指定介面的流量減少了攻擊面,避免不必要的網路暴露。
移除Setuid與Setgid二進位檔案
大多數容器化應用程式不需要任何setuid或setgid二進位檔案。如果能夠停用或移除這些二進位檔案,可以阻止它們被用於許可權提升攻擊。
取得映像檔中此類別二進位檔案的列表:
docker run debian find / -perm +6000 -type f -exec ls -ld {} \; 2> /dev/null
在Dockerfile中移除這些特殊許可權:
FROM debian:wheezy
RUN find / -perm +6000 -type f -exec chmod a-s {} \; || true
這個命令找出所有具有setuid或setgid位的檔案並移除這些特殊許可權。
限制記憶體使用
限制記憶體可以防止拒絕服務攻擊與記憶體洩漏應用程式消耗主機上的所有記憶體。Docker的記憶體與交換記憶體標誌可以限制容器使用的資源量:
# 限制容器只能使用128MB記憶體,無交換記憶體
docker run -m 128m --memory-swap 128m amouat/stress \
stress --vm 1 --vm-bytes 127m -t 5s
memory-swap引數設定的是記憶體加上交換記憶體的總量。適當的記憶體限制確保容器不會影響主機與其他容器的正常運作。
限制CPU使用
如果攻擊者能讓容器使用主機上的所有CPU,將導致其他容器資源匱乏。Docker中CPU份額由相對權重決定,預設值為1024:
docker run -d --name load1 -c 2048 amouat/stress
docker run -d --name load2 amouat/stress
docker run -d --name load3 -c 512 amouat/stress
相對權重意味著在預設設定下,任何容器都不可能使其他容器資源匱乏。可以為特定容器組分配較低的預設值以確保公平性。
也可以使用CFS透過cpu-period與cpu-quota標誌分享CPU:
docker run -d --cpu-period=50000 --cpu-quota=25000 myimage
這設定容器在50000微秒的週期內最多可以使用25000微秒的CPU時間,相當於50%的CPU使用率。
容器重啟策略
頻繁重啟的容器可能會耗盡系統資源。使用on-failure重啟策略而非always:
docker run -d --restart=on-failure:10 my-flaky-image
這設定容器最多重啟10次。Docker採用指數退避策略進行容器重啟,有效防止了利用重啟功能的拒絕服務攻擊。
檔案系統限制
限制容器的寫入能力可以防止多種攻擊。使用read-only引數使容器的檔案系統唯讀:
docker run --read-only debian touch x
對於掛載的卷,新增ro標記使其唯讀:
docker run -v $(pwd):/pwd:ro debian touch /pwd/x
這種設定簡化了稽核流程,如果容器的檔案系統與其建立時的映像檔完全相同,只需對映像檔進行一次離線稽核。
限制容器核心能力
Linux核心定義了稱為capabilities的許可權集合,可以賦予程式更精細的系統存取許可權。Docker容器預設以有限的核心能力子集執行。
使用cap-add與cap-drop引數控制容器可用的能力:
# 移除所有能力
docker run --cap-drop all debian chown 100 /tmp
# 新增特定能力
docker run --cap-drop all --cap-add CHOWN debian chown 100 /tmp
這種方法大幅提升安全性,攻擊者即使入侵容器也會受到嚴格限制。
資源限制ulimits
Linux核心定義了可應用於程式的資源限制。這些限制也可應用於Docker容器:
CPU時間限制:
docker run --ulimit cpu=12:14 amouat/stress stress --cpu 1
檔案描述符限制:
docker run --ulimit nofile=5 debian cat /etc/hostname
程式數量限制:
docker run --user 500 --ulimit nproc=2 -d debian sleep 100
這些限制提供額外的安全保障,防止資源濫用。
Linux安全模組
SELinux設定
SELinux提供強制存取控制,根據型別與類別控制存取。在Docker環境中,SELinux預設政策旨在保護主機防範容器,以及保護容器不受其他容器影響。
啟用SELinux後掛載卷需要適當的標籤:
mkdir data
echo "hello" > data/file
docker run -v $(pwd)/data:/data debian cat /data/file
這會失敗因為許可權拒絕。套用容器標籤到資料:
chcon -Rt svirt_sandbox_file_t data
docker run -v $(pwd)/data:/data debian cat /data/file
Docker 1.7版本開始,在掛載卷時提供Z或z字尾可自動重新標記卷:
docker run -v $(pwd)/new_data:/new_data:Z debian cat /new_data/file
使用security-opt標記更改容器的標籤或停用標籤功能:
docker run -v $(pwd)/newfile:/file --security-opt label:disable \
debian sh -c 'echo "hello" > /file'
AppArmor設定
AppArmor透過對處理程式套用設定檔來工作,限制它們在Linux功能與檔案存取層級的許可權。Docker會自動為每個啟動的容器套用AppArmor設定檔。
如果AppArmor幹擾容器執行,透過傳遞security-opt為該容器關閉AppArmor:
docker run --security-opt="apparmor:unconfined" myimage
也可以為容器指定不同的設定檔:
docker run --security-opt="apparmor:PROFILE" myimage
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
package "Linux安全模組" {
[SELinux] as SEL
[AppArmor] as AA
[Seccomp] as SEC
}
package "容器保護" {
[型別強制] as TE
[設定檔限制] as PR
[系統呼叫過濾] as SF
}
SEL --> TE
AA --> PR
SEC --> SF
note right of SEL
強制存取控制
多層級安全
多類別安全
end note
note right of AA
簡化設定
檔案存取控制
能力限制
end note
@enduml容器安全稽核
定期進行稽核或審查確保系統保持清潔、更新並檢查是否發生安全漏洞。容器環境的稽核應檢查所有執行的容器是否使用最新映像檔,以及這些映像檔是否使用最新與安全的軟體。
應識別並檢查容器與其建立時使用的映像檔之間的任何差異。稽核應涵蓋其他非容器系統特定的領域,如檢查存取日誌、檔案許可權與資料完整性。自動化稽核可以定期執行以儘快檢測任何問題。
現有稽核工具
Docker Bench for Security是Docker官方發布的工具,能檢查系統是否符合網際網路安全中心發布的Docker基準檔案中的各項建議。
Lynis是開放原始碼稽核工具,包含多項與Docker執行相關的檢查專案。這些工具能協助自動化稽核流程,提高安全性檢查的效率與全面性。
容器事件回應策略
當安全事件發生時,Docker提供了多項功能使我們能快速回應並調查問題根源:
docker commit快速建立受感染系統的快照,保存證據用於後續分析。docker diff揭示攻擊者可能做出的變更,協助理解攻擊範圍。docker logs檢視可能包含攻擊痕跡的日誌,追蹤攻擊者的行動軌跡。
處理受感染容器時首要問題是:容器突破是否可能發生?如果攻擊者可能獲得對主機系統的存取權,需要重新安裝主機系統並從映像檔重新建立所有容器。如果確定攻擊僅限於容器內部,只需停止該容器並替換它。
絕不應該將受感染的容器重新投入使用,即使它包含基礎映像檔中沒有的資料或變更。在安全領域中,我們不能再信任這個容器。
有效的預防策略包括以某種方式限制容器,例如以唯讀檔案系統執行容器或限制容器的Linux capabilities。處理完立即情況並實施攻擊緩解措施後,可以分析之前提交的受感染映像檔,確定攻擊的確切原因與範圍。
Docker未來安全功能
Docker正在開發多項與安全相關的功能,這些功能很可能在閱讀本文時已經可用。
Seccomp安全計算模式
Linux的seccomp功能可用於限制程式能夠執行的系統呼叫。透過將seccomp與Docker整合,容器可以被限制在特定的系統呼叫集合內。
提議的Docker seccomp整合預設會拒絕32位元系統呼叫、舊網路與容器通常不需要存取的各種系統功能。其他呼叫可以在執行時被明確拒絕或允許:
docker run -d --security-opt seccomp:allow:clock_adjtime ntpd
這允許容器進行clock_adjtime系統呼叫,這是使用網路時間協定守護程式同步系統時間所必需的。
使用者名稱空間
Docker計劃支援將root使用者對映到主機上的非特權使用者。此外,預計會看到各種可用於Docker的安全工具的整合,可能採取容器安全設定檔案的形式。
目前各種安全工具與選項之間存在許多重疊,例如檔案存取可以透過使用SELinux、降低capabilities或使用read-only標記來限制。未來可能會有更統一的安全設定介面。
深度防禦策略
從本文討論可以看出,安全系統需要考慮多方面因素。最重要的建議是遵循深度防禦與最小許可權原則。這確保即使攻擊者成功入侵系統的某個元件,也無法獲得對整個系統的完全存取權,並且必須突破更多防線才能造成重大損害或存取敏感資料。
屬於不同使用者或操作敏感資料的容器群組應在與其他使用者的容器或執行公開介面的容器分開的虛擬機器中執行。容器公開的連線埠應受到嚴格控制,特別是當暴露於外部世界時,同時內部限制也能限制任何受感染容器的存取。
容器可用的資源與功能應限制為其目的所需的最小範圍,透過設定記憶體使用量、檔案系統存取與核心capabilities的限制。透過執行強化的核心與使用AppArmor或SELinux等安全模組,可以在核心層面提供進一步的安全保障。
透過監控與稽核能夠及早發現攻擊。稽核在容器系統中特別有趣,因為容器可以輕鬆地與它們建立的映像檔進行比較,以檢測可疑的變更。同時可以離線檢查映像檔,確保它們執行最新與安全的軟體版本。沒有狀態的受感染容器可以迅速替換為新版本。
容器在安全方面是一種積極因素,這得益於它們提供的額外隔離與控制層級。一個正確使用容器的系統將比沒有容器的等效系統更加安全。關鍵在於如何正確利用這些工具,實施多層次的安全策略,並保持持續的監控與更新。
只有透過深思熟慮的安全設計與持續的安全實踐,我們才能真正發揮容器技術在安全方面的優勢,建構穩固可靠的容器化應用環境。