
六種容器逃逸的反擊——從 Admission Control 到 Linux 安全模組,層層封鎖逃逸路徑。
所有指令都在 koad 專案目錄下執行。如果還沒 clone,請先參考 Day 1 的 Step 0。
開始前請確認以下環境就緒。
# 主機終端
kubectl get pods -n koad | grep escape
預期結果:
escape-privileged 1/1 Running ...
escape-cgroup 1/1 Running ...
escape-docker 1/1 Running ...
escape-ptrace 1/1 Running ...
Day 8–11 的逃逸 Pod 需要存在,才能驗證防禦策略是否能阻擋這些攻擊。
# 主機終端
kubectl get pods -n kyverno | head -3
# 主機終端
# 檢查 Node 是否支援 seccomp
kubectl get nodes -o jsonpath='{.items[0].status.conditions[?(@.type=="Ready")].status}'
# 檢查 AppArmor 是否載入
minikube ssh -- aa-status 2>/dev/null | head -3
Minikube 預設支援 seccomp 和 AppArmor。如果
aa-status報錯,表示 AppArmor 未啟用——seccomp 仍然可以獨立使用。
完成本日實作後,你將能夠:
容器逃逸的防禦建立在 Linux kernel 的多層安全機制之上。理解這些機制,才能明白後面的防禦配置為什麼有效。
傳統 Unix 的權限模型是「全有全無」——root 擁有一切,普通使用者什麼都沒有。Linux Capabilities 把 root 的超級權限拆分成 40+ 個獨立能力,讓程式只獲得它需要的最小權限:
| Capability | 允許的操作 | 逃逸風險 |
|---|---|---|
| CAP_SYS_ADMIN | mount、namespace 操作、cgroup 配置 | 極高——幾乎等同 root |
| CAP_SYS_PTRACE | ptrace 追蹤其他 process | 高——可注入程式碼到其他容器 |
| CAP_NET_ADMIN | 網路設備/路由/防火牆配置 | 中——可嗅探/修改網路流量 |
| CAP_DAC_OVERRIDE | 忽略檔案權限檢查 | 中——可讀寫任意檔案 |
| CAP_NET_RAW | 使用 RAW socket | 低——但可用於網路掃描 |
Docker 預設給容器 14 個 capabilities(如 CAP_CHOWN、CAP_NET_RAW)。Day 8–11 的逃逸大多需要 CAP_SYS_ADMIN,這個 capability 在 PSS Restricted 下會被 drop。
seccomp 是 Linux kernel 的 syscall 過濾機制。每當程式呼叫系統呼叫(syscall),kernel 會先檢查 seccomp 規則,決定允許或拒絕:
| 模式 | 行為 | 用途 |
|---|---|---|
| Strict | 只允許 read、write、exit、sigreturn 四個 syscall |
極端場景(幾乎不可用) |
| Filter (BPF) | 用 BPF 程式定義允許/拒絕的 syscall 清單 | 容器預設使用 |
| Disabled | 不過濾任何 syscall | 特權容器 |
Docker/containerd 的 RuntimeDefault profile 擋住約 40+ 個危險 syscall,包括:
mount / umount2——阻止容器掛載宿主機檔案系統(擋 S13)reboot / kexec_load——阻止重啟或載入新 kerneladd_key / keyctl——阻止操作 kernel keyringptrace——阻止追蹤其他 process(擋 S17)unshare——阻止建立新 namespaceAppArmor 是 Linux 的 MAC(Mandatory Access Control,強制存取控制)機制,和傳統的 DAC(Discretionary Access Control,自主存取控制)互補:
chmod、chown)→ root 可以繞過AppArmor profile 有三種模式:
| 模式 | 行為 | 用途 |
|---|---|---|
| Enforce | 違反規則的操作被拒絕並記錄 | 生產環境 |
| Complain | 違反規則的操作被允許但記錄 | 測試階段 |
| Unconfined | 不限制(預設) | 未配置 AppArmor |
AppArmor profile 可限制四類操作:檔案讀寫(路徑 + 權限)、網路存取(協議 + 埠)、Capability 使用、程式執行(哪些二進位可以 exec)。
PSS 是 K8s 內建的 Pod 安全分級制度,定義三個安全等級:
| 等級 | 設計理念 | 適用場景 |
|---|---|---|
| Privileged | 完全不限制 | 系統元件(kube-proxy、CNI) |
| Baseline | 禁止已知的高風險配置(hostNetwork、privileged) | 一般工作負載的最低要求 |
| Restricted | 在 Baseline 之上進一步限制(必須 drop ALL caps、只能用特定 volume types) | 安全敏感的工作負載 |
為什麼是三級而非兩級?因為某些系統元件(如 CNI plugin、日誌收集器)確實需要 host 權限,直接二分法(全開/全關)會讓這些元件無法運行。三級制讓管理員可以對不同 Namespace 套用不同等級。
當容器內的程式發出一個 syscall 時,kernel 的檢查順序:

seccomp 在最前面,成本最低(純 BPF 比對),能在早期攔截大量危險操作。這就是為什麼 seccomp + AppArmor + Capabilities 三者都要設定——每層擋的東西不完全重疊。
Day 8–11 展示了六種容器逃逸 + 一個核心 CVE,今天開始反擊。
| 逃逸手法 | 利用的配置 | 防禦措施 | 防禦層 |
|---|---|---|---|
| S13 Privileged mount | privileged: true | PSS Restricted | Admission |
| S14 Cgroup release_agent | privileged: true | PSS Restricted | Admission |
| S15 Docker.sock | hostPath Volume | Kyverno block-hostpath | Admission |
| S16 /proc core_pattern | privileged + hostPath | PSS + Kyverno | Admission |
| S17 SYS_PTRACE | hostPID + SYS_PTRACE | PSS + drop caps | Admission |
| S18 lxcfs | privileged: true | PSS Restricted | Admission |
| S19 Dirty Pipe | 核心漏洞 | seccomp + gVisor | Runtime |

Pod Security Standards 是 Kubernetes 1.25 起 GA 的內建安全策略機制,取代了已廢棄的 PodSecurityPolicy(PSP)。PSS 透過 Namespace label 控制,不需要安裝任何額外元件。
| 層級 | 限制 | 阻擋的逃逸 | 適用場景 |
|---|---|---|---|
| Privileged | 無限制 | 無 | kube-system 等系統 NS |
| Baseline | 阻擋已知危險配置 | S13, S14, S17, S18 | 一般應用 |
| Restricted | 最嚴格限制 | 全部六式 | 生產環境 |
| 配置項 | Baseline | Restricted | 逃逸關聯 |
|---|---|---|---|
| privileged | 禁止 | 禁止 | S13, S14, S18 |
| hostPID | 禁止 | 禁止 | S17 |
| hostIPC | 禁止 | 禁止 | — |
| hostNetwork | 允許 | 禁止 | S23 |
| hostPath | 允許 | 允許(但其他限制擋住) | S15, S16 |
| capabilities.add | 限制清單 | 只允許 NET_BIND_SERVICE | S17 |
| runAsNonRoot | 不要求 | 要求 | — |
| drop ALL | 不要求 | 要求 | S17, S18 |
| seccomp | 不要求 | 要求 RuntimeDefault | S14, S17 |
| allowPrivilegeEscalation | 允許 | 禁止 | — |
| volumes | 允許 hostPath | 限制類型清單 | S15, S16 |
踩坑提醒:PSS Baseline 不阻擋 hostPath Volume!S15(docker.sock)和 S16(/proc)需要 Restricted 或 Kyverno 額外策略來阻擋。這是 Baseline 最大的盲區。
# Restricted PSS 要求的配置:
spec:
hostPID: false # 禁止 → 擋住 S17
hostIPC: false # 禁止
hostNetwork: false # 禁止
containers:
- securityContext:
privileged: false # 禁止 → 擋住 S13, S14, S16, S18
runAsNonRoot: true # 要求
allowPrivilegeEscalation: false # 禁止
capabilities:
drop: ["ALL"] # 必須 drop ALL → 擋住 SYS_PTRACE
add: ["NET_BIND_SERVICE"] # 最多只能加這一個
seccompProfile:
type: RuntimeDefault # 要求 seccomp
volumes: # 允許的 Volume 類型
- configMap # ✓
- secret # ✓
- emptyDir # ✓
- persistentVolumeClaim # ✓
- downwardAPI # ✓
- projected # ✓
- csi # ✓
- ephemeral # ✓
# hostPath → 不在清單中 → 擋住 S15, S16
PSS 透過 Namespace label 啟用,三種模式可以組合使用:
| 模式 | 效果 | 何時使用 |
|---|---|---|
| enforce | 拒絕不合規的 Pod | 確認安全後 |
| warn | 允許但顯示警告 | 評估影響 |
| audit | 允許但寫入 audit log | 記錄違規 |
# 推薦的漸進式啟用策略:
# 步驟 1:先用 warn + audit(不影響現有 Pod)
kubectl label namespace production \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
# 步驟 2:觀察一週,確認沒有合法 Pod 被標記
kubectl get events -n production | grep PodSecurity
# 步驟 3:確認安全後啟用 enforce
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted
# 驗證
$ kubectl get ns production --show-labels
NAME STATUS AGE LABELS
production Active 2h pod-security.kubernetes.io/enforce=restricted,...
# 嘗試建立特權 Pod(應該被拒絕)
$ kubectl run test --image=busybox -n production \
--overrides='{"spec":{"containers":[{"name":"test","image":"busybox",
"securityContext":{"privileged":true}}]}}'
Error from server (Forbidden): pods "test" is forbidden:
violates PodSecurity "restricted:latest":
privileged (container "test" must not set securityContext.privileged=true),
allowPrivilegeEscalation != false,
unrestricted capabilities,
runAsNonRoot != true,
seccompProfile
# 可以鎖定 PSS 版本,避免升級 K8s 時規則變更影響應用
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=v1.30
| 項目 | PSS(內建) | Kyverno |
|---|---|---|
| 需安裝 | 否(K8s 內建) | 是(Helm 安裝) |
| 策略粒度 | 三個固定層級 | 完全自定義 |
| 例外處理 | Namespace + Pod label exempt | 可按 label/name/image/SA 排除 |
| hostPath 控制 | Restricted 限制 volume 類型 | 可精確到路徑(如只擋 /var/run) |
| 報告 | audit-annotations | PolicyReport CRD(詳細) |
| 學習曲線 | 低 | 中 |
| 建議 | 作為基線,全域啟用 | 作為進階,補充 PSS 不足 |
最佳實踐:PSS 作為基線 + Kyverno 作為補充。 PSS 擋大部分危險配置,Kyverno 處理 PSS 管不到的(如特定 hostPath 路徑、映像來源限制、label 要求)。
seccomp(Secure Computing Mode)限制 process 可以使用的系統呼叫。Linux 有 400+ 個 syscall,但大部分容器只需要其中 50–100 個。seccomp profile 允許你明確定義白名單。
| 模式 | 說明 | 效果 |
|---|---|---|
| disabled (0) | 不限制 | 特權容器預設 |
| strict (1) | 只允許 read/write/exit/sigreturn | 太嚴格,幾乎無法使用 |
| filter (2) | 依照 profile 過濾 | 容器的正常模式 |
# 確認 seccomp 模式
$ grep Seccomp /proc/self/status
Seccomp: 2 # filter mode
Seccomp_filters: 1 # 載入了 1 個 filter
Docker 和 containerd 的預設 seccomp profile 已經阻擋了 40+ 個危險 syscall:
| 被阻擋的 syscall | 作用 | 阻擋的逃逸 |
|---|---|---|
| mount / umount2 | 掛載檔案系統 | S13 |
| ptrace | Process 追蹤 | S17 |
| mknod / mknodat | 建立裝置節點 | S18 |
| reboot | 重啟系統 | — |
| init_module / finit_module | 載入核心模組 | rootkit |
| kexec_load | 載入新核心 | — |
| pivot_root | 改變根目錄 | — |
| swapon / swapoff | 交換空間管理 | — |
踩坑提醒:
privileged: true會完全關閉 seccomp。所以 S13–S14 的攻擊容器不受 seccomp 限制。這也是為什麼禁止 privileged 是最重要的防禦。
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_AARCH64"],
"syscalls": [
{
"names": [
"accept4", "access", "arch_prctl", "bind",
"brk", "clone", "close", "connect",
"dup", "dup2", "epoll_create1", "epoll_ctl",
"epoll_wait", "execve", "exit", "exit_group",
"fcntl", "fstat", "futex", "getcwd",
"getdents64", "getpid", "getppid", "getsockname",
"getsockopt", "ioctl", "listen", "lseek",
"mmap", "mprotect", "munmap", "nanosleep",
"newfstatat", "openat", "pipe2", "poll",
"read", "readlink", "recvfrom", "rt_sigaction",
"rt_sigprocmask", "sendto", "set_tid_address",
"setsockopt", "socket", "stat", "write"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
defaultAction: SCMP_ACT_ERRNO = 預設拒絕所有 syscall,只允許白名單中的。注意這個 profile 沒有 mount、ptrace、mknod——所以 S13、S17、S18 即使在非特權容器中也會被阻擋。
| 逃逸 | 使用的 syscall | RuntimeDefault 是否擋 | 自訂 profile 是否擋 |
|---|---|---|---|
| S13 mount | mount | 是 | 是 |
| S14 cgroup | open + write + mount | mount 被擋 | 是 |
| S16 core_pattern | open + write | 部分(write 到 /proc) | 可以 |
| S17 ptrace | ptrace | 是 | 是 |
| S18 lxcfs | mknod | 是 | 是 |
| S19 Dirty Pipe | pipe + splice + write | 否(合法 syscall) | 困難 |
# 方法 1:使用 RuntimeDefault(推薦)
apiVersion: v1
kind: Pod
spec:
securityContext:
seccompProfile:
type: RuntimeDefault # ← Docker/containerd 的預設 profile
containers:
- name: app
image: myapp:latest
# 方法 2:使用自訂 profile
apiVersion: v1
kind: Pod
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: "profiles/koad-restricted.json"
containers:
- name: app
image: myapp:latest
自訂 profile 需要先把 JSON 檔案放到節點的 /var/lib/kubelet/seccomp/profiles/ 目錄。
踩坑提醒:如果 Pod 使用了不存在的 Localhost profile,Pod 會一直處於
CreateContainerError狀態。確認 profile 檔案已經部署到所有節點上。可以用 DaemonSet 來分發 seccomp profile。
# 在 strace 模式下觀察哪些 syscall 被阻擋
$ strace -c -f <command> 2>&1 | grep EPERM
# 可以看到哪些 syscall 因為 seccomp 被拒絕
AppArmor 是 Linux 安全模組(LSM),透過 profile 限制 process 可以存取的檔案、網路和 capability。與 seccomp 的差異:
| 項目 | seccomp | AppArmor |
|---|---|---|
| 限制層級 | syscall 級別 | 路徑 + 操作級別 |
| 限制對象 | 系統呼叫(如 mount, ptrace) | 檔案路徑 + capability + network |
| 粒度 | syscall 名稱 | 路徑 pattern + rwx 權限 |
| 設定格式 | JSON profile | 文字 profile(語法類似 C) |
| 適用場景 | 阻擋不該用的 syscall | 限制檔案存取範圍 |
| 替代方案 | — | SELinux(Red Hat 系) |
#include <tunables/global>
profile koad-restricted flags=(attach_disconnected) {
#include <abstractions/base>
# === 允許:應用基本運行 ===
/etc/passwd r,
/etc/group r,
/etc/nsswitch.conf r,
/etc/resolv.conf r,
/etc/hosts r,
/etc/ssl/** r,
# 允許應用目錄
/app/** r,
/app/bin/* ix,
# 允許 tmp
/tmp/** rw,
/var/tmp/** rw,
# === 禁止:逃逸相關操作 ===
# 禁止 mount 操作(擋 S13, S14, S18)
deny mount,
# 禁止 ptrace(擋 S17)
deny ptrace,
# 禁止存取敏感 /proc 路徑(擋 S16)
deny /proc/sys/kernel/** w, # 阻擋 core_pattern 修改
deny /proc/sys/kernel/modprobe w, # 阻擋 modprobe_path 修改
deny /proc/sysrq-trigger w, # 阻擋 SysRq
# 禁止存取 cgroup(擋 S14)
deny /sys/fs/cgroup/** w,
# 禁止 docker.sock(擋 S15)
deny /var/run/docker.sock rw,
deny /run/docker.sock rw,
deny /run/containerd/containerd.sock rw,
# 禁止 raw network(阻擋 ARP spoofing 等)
deny network raw,
# 禁止存取敏感目錄
deny /etc/shadow r,
deny /etc/kubernetes/** rw,
deny /var/lib/kubelet/** rw,
}
# 1. 將 profile 檔案放到節點上
$ sudo cp koad-restricted /etc/apparmor.d/koad-restricted
# 2. 載入 profile
$ sudo apparmor_parser -r -W /etc/apparmor.d/koad-restricted
# 3. 確認載入
$ sudo aa-status | grep koad
koad-restricted (enforce)
# 4. 確認模式
$ sudo aa-status
apparmor module is loaded.
27 profiles are loaded.
27 profiles are in enforce mode.
...
koad-restricted
# 5. 在 Pod 中套用(K8s 1.30+ 使用 securityContext)
apiVersion: v1
kind: Pod
metadata:
name: hardened-app
spec:
containers:
- name: app
image: myapp:latest
securityContext:
appArmorProfile:
type: Localhost
localhostProfile: koad-restricted
# K8s 1.29 及更早版本使用 annotation
apiVersion: v1
kind: Pod
metadata:
annotations:
container.apparmor.security.beta.kubernetes.io/app: localhost/koad-restricted
spec:
containers:
- name: app
image: myapp:latest
K8s 1.30 重大變更:AppArmor 從 beta annotation 正式升級為 GA
securityContext欄位。1.30+ 應使用securityContext.appArmorProfile(如上方第一個範例),annotation 方式仍然相容但已標記為 deprecated。CKS 考試建議優先使用新語法。兩種語法同時存在時,securityContext優先。RuntimeDefault profile 的新舊寫法對照:
版本 寫法 位置 | 1.30+ |
securityContext.appArmorProfile.type: RuntimeDefault| Pod/Container spec |
| < 1.30 |container.apparmor.security.beta.kubernetes.io/<name>: runtime/default| Pod annotation |
踩坑提醒:AppArmor profile 必須在節點上預先載入。如果 Pod 指定了不存在的 profile,Pod 會
CreateContainerError。建議用 DaemonSet + init container 在所有節點上載入 profile。
| 逃逸 | AppArmor 規則 | 效果 |
|---|---|---|
| S13 mount | deny mount |
阻擋 mount 設備 |
| S14 cgroup | deny /sys/fs/cgroup/** w |
阻擋 cgroup 寫入 |
| S15 docker.sock | deny /var/run/docker.sock rw |
阻擋 socket 存取 |
| S16 core_pattern | deny /proc/sys/kernel/** w |
阻擋 core_pattern 修改 |
| S17 ptrace | deny ptrace |
阻擋 ptrace 呼叫 |
| 項目 | AppArmor | SELinux |
|---|---|---|
| 預設發行版 | Ubuntu, SUSE | RHEL, CentOS, Fedora |
| 控制模型 | 路徑為主 | 標籤為主 |
| 學習曲線 | 低 | 高 |
| 細粒度 | 中 | 高 |
| K8s 支援 | 1.4+ (beta) → 1.30 GA | 支援但更複雜 |
securityContext:
capabilities:
drop: ["ALL"] # 先移除全部
add: ["NET_BIND_SERVICE"] # 只加回需要的(綁定 <1024 port)
| 應用類型 | 需要的 Capability | 說明 |
|---|---|---|
| Web server (port 80) | NET_BIND_SERVICE | 綁定 port 80/443 |
| 不需 port <1024 | 無 | drop ALL 即可 |
| 需要 ping | NET_RAW | ICMP 需要 raw socket |
| 時間同步 | SYS_TIME | 修改系統時間 |
| 容器 runtime | 很多 | 必須在 kube-system 中 |
| 逃逸 | 需要的 Capability | drop ALL 是否阻擋 | 備註 |
|---|---|---|---|
| S13 | 全部(privileged) | 是 | privileged 繞過 |
| S14 | SYS_ADMIN | 是 | 需要 mount cgroup |
| S15 | 不需要特別 cap | 否 | 只需要 hostPath |
| S16 | SYS_ADMIN | 是 | 需要寫 /proc |
| S17 | SYS_PTRACE | 是 | 明確 add 才有 |
| S18 | SYS_ADMIN + MKNOD | 是 | 需要 mknod |

理論講完了,現在動手驗證——用 Day 8–11 的逃逸手法重新攻擊,確認防禦真的有效。
# 主機終端 — 在 koad namespace 啟用 PSS Restricted(warn 模式觀察)
kubectl label namespace koad \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted \
--overwrite
# 切換到 enforce 模式
kubectl label namespace koad \
pod-security.kubernetes.io/enforce=restricted \
--overwrite
# 主機終端 — 嘗試重新建立特權 Pod
cat <<'EOF' | kubectl apply -n koad -f -
apiVersion: v1
kind: Pod
metadata:
name: escape-privileged-test
spec:
containers:
- name: attacker
image: ubuntu:22.04
command: ["sleep", "infinity"]
securityContext:
privileged: true
EOF
預期結果:
Error from server (Forbidden): error when creating "STDIN": pods "escape-privileged-test"
is forbidden: violates PodSecurity "restricted:latest":
privileged (container "attacker" must not set securityContext.privileged=true), ...
PSS Restricted 在 Admission 階段直接拒絕特權 Pod,S13 逃逸無法啟動。
# 主機終端 — 嘗試建立需要 SYS_PTRACE + hostPID 的 Pod
cat <<'EOF' | kubectl apply -n koad -f -
apiVersion: v1
kind: Pod
metadata:
name: escape-ptrace-test
spec:
hostPID: true
containers:
- name: attacker
image: ubuntu:22.04
command: ["sleep", "infinity"]
securityContext:
capabilities:
add: ["SYS_PTRACE"]
EOF
預期結果:
Error from server (Forbidden): error when creating "STDIN": pods "escape-ptrace-test"
is forbidden: violates PodSecurity "restricted:latest":
host namespaces (hostPID=true),
unrestricted capabilities (container "attacker" must not include "SYS_PTRACE" in ...
PSS 同時擋住 hostPID 和 SYS_PTRACE capability,S17 逃逸的兩個前提都被封鎖。
# 主機終端 — 嘗試建立掛載 docker.sock 的 Pod
cat <<'EOF' | kubectl apply -n koad -f -
apiVersion: v1
kind: Pod
metadata:
name: escape-docker-test
spec:
containers:
- name: attacker
image: ubuntu:22.04
command: ["sleep", "infinity"]
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
volumes:
- name: docker-sock
hostPath:
path: /var/run/docker.sock
EOF
預期結果:
Error from server (Forbidden): error when creating "STDIN": pods "escape-docker-test"
is forbidden: violates PodSecurity "restricted:latest":
restricted volume types (volumes "docker-sock" uses restricted volume type "hostPath"), ...
PSS Restricted 禁止 hostPath volume type,S15 Docker.sock 逃逸被封鎖。這正是 Day 12 提到的 Baseline 不擋 hostPath 但 Restricted 會擋的關鍵差異。
# 主機終端 — 建立一個套用 seccomp RuntimeDefault 的非特權 Pod
cat <<'EOF' | kubectl apply -n koad -f -
apiVersion: v1
kind: Pod
metadata:
name: seccomp-test
spec:
containers:
- name: test
image: ubuntu:22.04
command: ["sleep", "infinity"]
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
EOF
# 等待 Pod 啟動
kubectl wait --for=condition=Ready pod/seccomp-test -n koad --timeout=30s
# 嘗試在 Pod 內執行 mount(S13 的核心操作)
kubectl exec -n koad seccomp-test -- mount -t proc proc /tmp 2>&1 || true
預期結果:
mount: /tmp: permission denied.
即使 Pod 非特權地啟動成功,seccomp RuntimeDefault 仍然阻擋 mount syscall。這是第二層防禦。
# 主機終端 — 清理驗證用的 Pod
kubectl delete pod seccomp-test -n koad 2>/dev/null
# 如需還原 PSS(讓 Day 8-11 逃逸 Pod 可以重建)
kubectl label namespace koad \
pod-security.kubernetes.io/enforce- \
pod-security.kubernetes.io/warn- \
pod-security.kubernetes.io/audit-
驗證結論:PSS Restricted 在 Admission 層直接拒絕了 S13(privileged)、S15(hostPath)、S17(hostPID + SYS_PTRACE)三種逃逸所需的 Pod 配置。seccomp RuntimeDefault 在 Runtime 層阻擋了 mount 等危險 syscall。兩層防禦互相補位——即使某一層被繞過,另一層仍然有效。
| 考點 | 本日內容 | 權重 |
|---|---|---|
| Pod Security Standards | Restricted 層級設定 | Minimize Microservice 20% |
| seccomp | Profile 建立和套用 | System Hardening 10% |
| AppArmor | Profile 載入和套用 | System Hardening 10% |
| Capabilities | drop ALL + add 最小集 | Minimize Microservice 20% |
題目:為 Pod web-app 套用 seccomp RuntimeDefault profile 和 AppArmor runtime/default profile。
apiVersion: v1
kind: Pod
metadata:
name: web-app
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: web
image: nginx:1.25
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
appArmorProfile:
type: RuntimeDefault
如果你需要還原環境以重新操作 Day 8–11 的逃逸:
# 主機終端 — 移除 PSS label(讓逃逸 Pod 可以再次建立)
kubectl label namespace koad \
pod-security.kubernetes.io/enforce- \
pod-security.kubernetes.io/warn- \
pod-security.kubernetes.io/audit-
# 移除自訂 seccomp profile(如果有部署到 Node)
minikube ssh -- sudo rm -f /var/lib/kubelet/seccomp/profiles/koad-*.json
移除防禦後,重新部署攻擊 Pod 即可恢復 Day 8–11 的逃逸環境。
| 問題 | 原因 | 解法 |
|---|---|---|
| seccomp profile 套用後 Pod 無法啟動 | Profile 路徑錯誤或 JSON 格式不合法 | 確認 profile 放在 /var/lib/kubelet/seccomp/profiles/;用 jq . profile.json 驗證 JSON 語法 |
| PSS enforce 模式沒有攔擋違規 Pod | Label 設定層級不對或只用了 warn/audit | 確認 namespace label 為 pod-security.kubernetes.io/enforce: restricted,不是 warn 或 audit |
AppArmor 不可用:AppArmor is not enabled |
Minikube 驅動或 Node OS 不支援 AppArmor | minikube ssh -- cat /sys/module/apparmor/parameters/enabled 確認;Docker driver 支援,containerd driver 需額外配置 |
| seccomp profile 看不到攔截效果 | RuntimeDefault 已允許大部分常用 syscall | RuntimeDefault 只擋高危 syscall(如 reboot);要擋 mount 等逃逸 syscall 需自訂 profile |
| Kyverno policy 未生效 | Policy 的 validationFailureAction 設為 Audit 而非 Enforce |
修改 policy 將 validationFailureAction 改為 Enforce,重新 apply |
RuntimeDefault 或自訂)三者的防禦範圍互補,不是擇一使用。建議的啟用順序:
明天 Day 13 防禦逃逸(下)——gVisor 沙箱和容器不變性,用隔離和最小化進一步縮小攻擊面。