許多企業為了在特定司法管轄區開展業務,需要遵守各種法規。這些法規可能是適用於特定地區的一般性法規,如歐盟的《一般資料保護條例》(GDPR),或針對特定行業的法規,如PCI-DSS或沙賓法案(Sarbanes-Oxley)。
無論法規的目的是為了保護終端使用者隱私、保護投資還是增加問責制,都需要確保熟悉這些要求,並找到實施這些政策和控制的方法。
Kubernetes環境中的法規遵循策略
在Kubernetes環境中實作法規遵循時,玄貓建議從技術角度考慮以下幾點:
- 資料隔離與本地化:確保敏感資料儲存在符合法規要求的地理位置
- 存取控制與稽核:實施細粒度的RBAC策略並維護全面的稽核日誌
- 加密策略:在傳輸中和靜態狀態下加密敏感資料
- 合規自動化:使用政策引擎(如OPA)自動執行合規檢查
# 使用OPA Gatekeeper強制執行GDPR相關政策的範例
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sGDPRCompliance
metadata:
name: prevent-eu-data-exfiltration
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- eu-customer-data
parameters:
allowedEgressEndpoints:
- eu-central-1.amazonaws.com
- eu-west-1.amazonaws.com
forbiddenEgressEndpoints:
- us-*.amazonaws.com
- ap-*.amazonaws.com
這個YAML設定展示瞭如何使用OPA Gatekeeper來實施GDPR相關的政策約束。這個自定義的約束名為"K8sGDPRCompliance",專門針對包含歐盟客戶資料的名稱空間。它定義了兩組規則:允許的出口端點(僅限歐盟區域的AWS服務)和禁止的出口端點(所有美國和亞太區域的AWS服務)。這種設定可以防止歐盟使用者資料被意外傳輸到不符合GDPR要求的地區,從而幫助組織維持法規遵循。這是一個技術控制的例子,展示瞭如何將法規要求轉化為具體的Kubernetes策略。
在實施這些技術控制時,務必與法律團隊合作,確保所採取的措施完全符合適用的法規要求。技術實施只是法規遵循的一部分,還需要配合適當的政策、程式和培訓。
安全的人性化挑戰
在處理技術安全問題時,我們常發現技術反而是"簡單"的部分,而最薄弱的環節往往是人。不同的環境,如雲端或本地佈署,提供了不同的權衡。沒有絕對的對錯,只有對自己優先事項的認識,以及對希望自己擁有哪些部分和希望解除安裝哪些部分的清晰理解。
建立人性化的安全文化
技術控制固然重要,但建立強大的安全文化同樣不可或缺。玄貓發現,最成功的安全計劃通常包含以下人性化元素:
- 安全意識培訓:定期與引人入勝的培訓,而非枯燥的合規檢查項
- 激勵機制:獎勵發現和報告安全問題的行為
- 無責備文化:鼓勵開放報告安全事件,不懲罰誠實的錯誤
- 安全冠軍計劃:在各團隊中培養安全倡導者
- 實用性優先:確保安全措施不會過度妨礙工作效率
在Kubernetes環境中,這種平衡尤為重要。過於嚴格的控制可能導致團隊尋找繞過方法,而過於寬鬆的政策則可能導致安全漏洞。
成功的安全策略需要在技術控制和人性化考量之間取得平衡,同時適應組織的特定需求和文化。
安全實踐的持續演進
安全不是一次性的專案,而是持續的旅程。隨著技術和威脅景觀的演變,安全實踐也必須不斷調整和改進。
在Kubernetes環境中,這意味著:
- 持續監控新的威脅和漏洞
- 定期審查和更新安全政策和控制
- 不斷學習和適應新的安全最佳實踐
- 與更廣泛的安全社群保持聯絡,分享知識和經驗
安全是一個永無止境的過程,但透過結合技術控制、人性化考量和持續改進,可以建立一個強大與可持續的安全姿態。
在技術快速發展的今天,安全已不再是單純的技術問題,而是需要全面考慮人、流程和技術的綜合挑戰。透過理解SLO如何被武器化、社交工程的常見手法以及法規遵循的複雜性,我們可以建立更全面、更有效的安全防護體系,保護我們的Kubernetes環境免受各種威脅。
容器安全檢測與分析實戰
理解容器內的系統資訊
當我們需要評估容器安全性時,瞭解容器內部的系統資訊至關重要。容器內的程式可以透過檢查各種系統檔案來取得有關其執行環境的詳細訊息。首先,讓我們看如何檢查容器的cgroup設定:
cat /proc/self/cgroup
這會顯示類別似以下的輸出:
4:cpuset:/kubepods/besteffort/pod8a6fa26b-...
3:hugetlb:/kubepods/besteffort/pod8a6fa26b-...
2:devices:/kubepods/besteffort/pod8a6fa26b-...
1:name=systemd:/kubepods/besteffort/pod8a6fa26b-...
這個輸出揭示了容器執行在Kubernetes環境中,並且是以"besteffort"服務品質級別執行的。透過cgroup路徑,攻擊者可以識別出這是一個Kubernetes Pod,甚至可以從路徑中提取Pod的唯一識別符號。
程式狀態分析
容器內的程式還可以透過檢查/proc/self/status
來取得有關自身的詳細訊息:
cat /proc/self/status
此命令會顯示程式的各種屬性,包括:
- 程式名稱和狀態
- 程式ID和父程式ID
- 使用者和組ID
- 記憶體使用情況
- 執行緒數量
- 能力(capabilities)設定
這些訊息對攻擊者非常有價值,特別是能力(capabilities)部分。Linux能力系統允許將特權細分為不同的單元,而不是簡單的"root/非root"二分法。透過檢查CapEff
、CapPrm
和CapBnd
欄位,攻擊者可以瞭解容器內程式擁有哪些特權,這對於規劃提權攻擊至關重要。
解析容器能力
對於安全稽核,我們可以使用以下命令提取並解析關鍵的安全相關訊息:
grep -E '(Uid|CoreDumping|Seccomp|NoNewPrivs|Cap[A-Za-z]+):' /proc/self/status
輸出可能如下:
Uid: 0 0 0 0
CoreDumping: 0
CapInh: 0000003fffffffff
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
這個輸出顯示程式以root使用者(Uid: 0)執行,並且擁有大量的能力。NoNewPrivs: 0
表示沒有啟用"no new privileges"限制,Seccomp: 0
表示沒有啟用seccomp過濾器。這些都是潛在的安全風險。
解碼Linux能力
能力值以十六進製表示,需要解碼才能理解:
capsh --decode=0000003fffffffff
這會列出所有啟用的能力,例如:
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,...
這些能力決定了容器內程式可以執行的特權操作。例如,cap_sys_admin
允許執行許多系統管理任務,cap_net_admin
允許設定網路,而cap_sys_ptrace
允許除錯其他程式。在安全的容器設定中,這些能力應該被嚴格限制。
使用專業工具檢查容器安全
除了基本的系統命令外,還有專門設計用於容器安全檢查的工具。capsh --print
提供了當前程式的能力概覽:
capsh --print
輸出包括:
Current: =cap_chown,cap_dac_override,cap_fowner,...+eip
Bounding set =cap_chown,cap_dac_override,cap_fowner,...
Ambient set =
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
secure-no-ambient-raise: no (unlocked)
uid=0(root)
gid=0(root)
groups=1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy)...
這個輸出提供了更詳細的安全設定訊息,包括各種能力集(current、bounding、ambient)以及securebit設定。這些訊息有助於瞭解容器的安全邊界和潛在的提權途徑。
使用amicontained進行全面檢查
amicontained
是一個專門設計用於檢測容器安全設定的工具:
export AMICONTAINED_SHA256="d8c49e2cf44ee9668219acd092ed961fc1aa420a6e036e0822d7a31033776c9f"
curl -fSL "https://github.com/genuinetools/amicontained/releases/download/v0.4.9/amicontained-linux-amd64" \
-o "/tmp/amicontained" \
&& echo "${AMICONTAINED_SHA256} /tmp/amicontained" | sha256sum -c - \
&& chmod a+x "/tmp/amicontained"
/tmp/amicontained
執行後會顯示全面的安全設定訊息:
Container Runtime: kube
Has Namespaces:
pid: true
user: false
AppArmor Profile: docker-default (enforce)
Capabilities:
BOUNDING -> chown dac_override fowner fsetid kill setgid setuid setpcap net_bind_service net_raw sys_chroot mknod audit_write setfcap
Seccomp: disabled
Blocked system calls (26):
SYSLOG SETUID SETSID SETREUID SETGROUPS SETRESUID VHANGUP PIVOT_ROOT ACCT SETTIMEOFDAY UMOUNT2 SWAPON SWAPOFF REBOOT SETHOSTNAME SETDOMAINNAME INIT_MODULE DELETE_MODULE LOOKUP_DCOOKIE KEXEC_LOAD FUTIMESAT UTIMENSAT FANOTIFY_INIT OPEN_BY_HANDLE_AT FINIT_MODULE KEXEC_FILE_LOAD
Looking for Docker.sock
這個工具提供了一站式的容器安全設定檢查,包括:
- 容器執行時(kube)
- 名稱空間設定(pid名稱空間啟用,user名稱空間未啟用)
- AppArmor設定檔案
- 能力設定
- Seccomp過濾器狀態(已停用)
- 被阻止的系統呼叫列表
- Docker socket檢測(用於許可權提升)
這些訊息對於安全稽核和漏洞評估非常寶貴。
檢查容器資源限制
容器的資源限制也是安全設定的重要部分:
free -m
可能顯示:
total used free shared buff/cache available
Mem: 3950 334 1473 6 2142 3327
Swap: 0 0 0
但這個命令使用的是主機級API,不會顯示容器的實際限制。要檢視容器的真實記憶體限制:
cat /sys/fs/cgroup/memory/memory.limit_in_bytes
如果設定了4MB的限制,輸出將是:
4194304
瞭解容器的資源限制對於防止拒絕服務攻擊很重要。攻擊者可以利用沒有適當資源限制的容器來耗盡系統資源,影響其他容器或主機的正常執行。
Kubernetes環境變數洩露
Kubernetes會在每個容器中設定許多環境變數,這些可能洩露敏感訊息:
env | grep -E '(KUBERNETES|[^_]SERVICE)_PORT=' | sort
輸出可能包含服務發現訊息:
ADSERVICE_PORT=tcp://10.3.253.186:9555
CARTSERVICE_PORT=tcp://10.3.251.123:7070
CHECKOUTSERVICE_PORT=tcp://10.3.240.26:5050
...
這些環境變數揭示了叢集內部的服務拓撲和網路設定。攻擊者可以利用這些訊息進行橫向移動,嘗試存取其他服務。雖然環境變數便於應用程式設定,但12因素應用程式建議將設定和機密放在環境變數中,這並不總是安全的做法,因為環境變數可以被PID名稱空間中的其他程式輕易讀取。
容器安全最佳實踐
根據以上分析,我總結出以下容器安全最佳實踐:
最小化容器內容:使用靜態、精簡或無發行版(distroless)容器,減少攻擊面。
限制網路工具:避免在容器中包含curl、wget等網路工具,以及具有網路函式庫的直譯器,防止攻擊者下載外部工具。
設定適當的資源限制:使用cgroups限制容器可使用的資源,防止DoS攻擊。
使用cgroups v2:相比cgroups v1,cgroups v2更安全,特別是在無root環境中。
限制容器能力:只授予容器執行所需的最小能力集。
啟用seccomp過濾器:阻止不必要的系統呼叫。
啟用使用者名稱空間:將容器內的root使用者對映到主機上的非特權使用者。
避免使用環境變數儲存機密:考慮使用專門的機密管理解決方案。
定期安全掃描:使用工具如amicontained檢查容器的安全設定。
網路隔離:實施嚴格的網路政策,限制容器之間的通訊。
容器安全是一個多層面的問題,需要從容器構建、設定到執行時保護的全面方法。透過瞭解容器內部可取得的訊息,可以更好地設計安全的容器環境,減少潛在的攻擊面。
Kubernetes 環境變數的安全風險
在容器化環境中,環境變數是一個經常被忽視的安全隱患。作為一名資安工程師,我發現許多團隊在管理 Kubernetes Secrets 時過於依賴環境變數,而這正是攻擊者最容易利用的弱點之一。
環境變數洩漏的真實風險
當你以 root 許可權或相同使用者身份檢視程式的環境時,所有的資訊都會一覽無遺。例如,我們可以使用以下命令檢視 PID 1 的環境變數:
tr '\0' '\n' < /proc/1/environ
這個簡單的命令會顯示類別似這樣的資訊:
HOSTNAME=9c7e824ed321
PWD=/
# ...其他環境變數
上述命令使用 tr
工具將環境變數檔案中的空字元(\0
)轉換為換行符(\n
),使環境變數以易讀的格式顯示。在 Linux 中,/proc/[pid]/environ
檔案包含程式的環境變數,但這些變數以空字元分隔,而非換行符,因此需要進行轉換才能正常閱讀。
環境變數的額外風險
環境變數洩漏的風險不僅限於惡意攻擊。在實際工作中,我經常看到應用程式在當機時會自動傾印其環境,將所有 Secrets 洩漏給任何能夠存取日誌系統的人。這種情況在微服務架構中尤為常見,因為日誌通常會被集中收集和分析。
更重要的是,透過環境變數掛載的 Kubernetes Secrets 存在一個關鍵缺陷:當 Secret 透過 API 伺服器更新後,容器中的環境變數不會自動更新。這意味著,即使你已經輪換了敏感憑證,容器仍在使用舊的值,直到容器重新啟動為止。
Secrets 掛載的安全最佳實踐
經過多次安全稽核,我認為掛載 Secrets 的最安全方式是使用檔案系統掛載點,而非環境變數。
檔案掛載的優勢
使用 tmpfs 檔案系統掛載 Secrets 有以下優勢:
- 攻擊者必須猜測或找到 Secret 檔案路徑,這比直接收集環境變數更困難
- 掛載的 Secrets 會在 kubelet 同步週期後自動更新
- 減少了應用程式當機日誌洩漏敏感資訊的風險
實作 Secret 檔案掛載
以下是一個將 Secret 掛載到 /etc/foo
路徑的範例:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
這個 YAML 設定義了一個名為 mypod
的 Pod,它使用 Redis 映像。最關鍵的部分是 volumeMounts
和 volumes
區段:
volumeMounts
定義了容器內的掛載點,這裡將名為foo
的卷掛載到容器內的/etc/foo
路徑,並設定為唯讀模式volumes
區段定義了名為foo
的卷,它的來源是名為mysecret
的 Kubernetes Secret
這種方法確保了 Secret 資料儲存在臨時檔案系統中,而與在 Secret 更新時,檔案內容會自動更新,無需重新啟動容器。
透過這種方式掛載 Secrets,能有效防止像 Captain Hashjack 這樣的攻擊者在竊取應用程式日誌時意外發現生產環境的敏感資訊。
容器檔案系統安全:tmpfs 與掛載點檢查
在容器安全領域,我發現檔案系統掛載點是容器逃逸的常見途徑。精明的攻擊者總是會檢查任何外部增加到掛載名稱空間的內容,這也是我在安全評估中的首要檢查專案。
容器安全基本原則
在容器安全設計中,有一個基本原則需要牢記:每個外部裝置、檔案系統、通訊端或分享到容器中的實體都會增加容器逃逸的風險。容器在僅包含基本操作必需品與不與其他容器或底層主機分享任何內容時,安全性最高。
檢查容器檔案系統掛載點
讓我們從搜尋常見容器檔案系統驅動程式 overlayfs 的掛載點開始,這可能洩漏有關設定案系統的容器執行時的資訊:
mount | grep overlay
輸出可能類別似於:
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/316/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/315/fs:...
這個命令搜尋所有掛載點中包含 overlay
的條目。從輸出中,我們可以看到:
- 底層容器執行時使用的檔案路徑包含
containerd
名稱 - 容器檔案系統在主機磁碟上的位置是
/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/
- 有多個分層目錄被列出,這些目錄在執行時被 overlayfs 組合成單一檔案系統
這些路徑是容器執行時預設設定的指紋,不同的容器執行時會有不同的檔案系統佈局。例如,runc 會以不同的方式洩漏其身份:
mount | grep overlay
可能會顯示:
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/3PTJCBKLNC2V5MRAEF3AU6EDMS:/var/lib/docker/overlay2/l/SAJGPHO7UFXGYFRMGNJPUOXSQ5:...
檢查掛載的 Secrets 和敏感資源
使用 df
命令可以檢視是否有任何 Secrets 被掛載到容器中:
df
在安全的容器中,輸出可能類別似於:
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 95G 6.6G 88G 7% /
tmpfs tmpfs 64M 0 64M 0% /dev
tmpfs tmpfs 7.1G 0 7.1G 0% /sys/fs/cgroup
/dev/sda1 ext4 95G 6.6G 88G 7% /etc/hosts
shm tmpfs 64M 0 64M 0% /dev/shm
tmpfs tmpfs 7.1G 0 7.1G 0% /proc/acpi
tmpfs tmpfs 7.1G 0 7.1G 0% /proc/scsi
tmpfs tmpfs 7.1G 0 7.1G 0% /sys/firmware
從這個輸出中,我們可以觀察到:
- tmpfs 被用於多個不同的掛載點
- 一些掛載點在
/proc
和/sys
中遮蔽了主機檔案系統 - 容器執行時對這些目錄中的特殊檔案執行了額外的遮蔽
在不安全的容器環境中,可能會看到更危險的掛載點:
Filesystem Type ... Use% Mounted on
tmpfs tmpfs ... 1% /etc/secret-volume
tmpfs tmpfs ... 1% /run/docker.sock
tmpfs tmpfs ... 1% /run/secrets/kubernetes.io/serviceaccount
容器逃逸的常見路徑
在我稽核的許多環境中,最容易與最方便的容器逃逸路徑是 /var/run/docker.sock
掛載點。這是主機上的容器執行時通訊端,它提供對在主機上執行的 Docker 守護程式的存取權。如果這些新容器具有特權,它們可以輕鬆地「逃離」容器名稱空間並以 root 身份存取底層主機。
高風險掛載點
其他具有吸引力的目標包括:
/var/run/secrets/kubernetes.io/serviceaccount
下的 Kubernetes 服務帳號令牌- 可寫入的主機掛載目錄,如
/etc/secret-volume
任何這些都可能導致容器逃逸,或協助攻擊者進行橫向移動。
Kubernetes 服務帳號的風險
kubelet 掛載到其容器中的所有內容對 kubelet 主機上的 root 使用者都是可見的。在容器內,kubectl 預設使用 /run/secrets/kubernetes.io/serviceaccount
中的憑證。在 kubelet 主機上,這些檔案掛載在 /var/lib/kubelet/pods/<pod-uid>/volumes/kubernetes.io~secret/<token-name>
下。
以下是一個在 Bash shell 中載入並使用這些憑證的命令:
kubectl-sa-dir () {
local DIR="${1:-}";
local API_SERVER="${2:-kubernetes.default}";
kubectl config set-cluster tmpk8s --server="https://${API_SERVER}" \
--certificate-authority="${DIR}/ca.crt";
kubectl config set-context tmpk8s --cluster=tmpk8s;
kubectl config set-credentials tmpk8s --token="$(<${DIR}/token)";
kubectl config set-context tmpk8s --user=tmpk8s;
kubectl config use-context tmpk8s;
kubectl get secrets -n null 2>&1 | sed -E 's,.*r "([^"]+).*,\1,g'
}
這個函式做了以下幾件事:
- 接受兩個引數:包含服務帳號憑證的目錄和 API 伺服器地址
- 使用目錄中的
ca.crt
憑證和 API 伺服器地址設定臨時 kubectl 叢集設定 - 建立一個新的 kubectl 上下文,並將其與剛建立的叢集關聯
- 從提供的目錄中讀取 token 檔案,並設定臨時使用者憑證
- 將臨時使用者與上下文關聯,並切換到該上下文
- 嘗試取得
null
名稱空間中的 secrets,以測試服務帳號的許可權
當針對特定目錄執行此命令時:
kubectl-sa-dir /var/lib/kubelet/pods/.../kubernetes.io~secret/priv-app-r4zkx/.../
如果成功,您可能會看到類別似這樣的輸出:
Cluster "tmpk8s" set.
Context "tmpk8s" created.
User "tmpk8s" set.
Context "tmpk8s" modified.
Switched to context "tmpk8s".
apiVersion: v1
clusters:
- cluster:
certificate-authority: /var/lib/kubelet/pods/.../kubernetes.io~secret/.../ca.crt
server: https://10.0.1.1:6443
name: tmpk8s
# ...
system:serviceaccount:kube-system:priv-app
此時,您已經設定了 kubectl 使用 system:serviceaccount:kube-system:priv-app
服務帳號,該帳號的設定現在位於您的 ~/.kube/config
中。攻擊者可以執行相同的操作—對 Kubernetes 節點的惡意 root 存取會揭露所有 Secrets!