iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Kubernetes

資安這條路:從攻擊者視角看 Kubernetes系列 第 12 篇

Day 12|防禦逃逸(上):Pod Security Standards + seccomp + AppArmor

  • 分享至 

  • xImage
  •  

資安這條路:從攻擊者視角看 Kubernetes

六種容器逃逸的反擊——從 Admission Control 到 Linux 安全模組,層層封鎖逃逸路徑。

前置準備

所有指令都在 koad 專案目錄下執行。如果還沒 clone,請先參考 Day 1 的 Step 0。

開始前請確認以下環境就緒。

1. 確認逃逸場景 Pod 運行中

# 主機終端
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 需要存在,才能驗證防禦策略是否能阻擋這些攻擊。

2. 確認 Kyverno 運行中

# 主機終端
kubectl get pods -n kyverno | head -3

3. 確認 seccomp 與 AppArmor 可用

# 主機終端
# 檢查 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 仍然可以獨立使用。

學習目標

完成本日實作後,你將能夠:

  • 設定 PSS Restricted 層級阻擋特權容器
  • 撰寫自訂 seccomp profile 阻擋危險 syscall(mount、ptrace、mknod)
  • 理解 AppArmor profile 的三種模式並套用到 Pod
  • 掌握三層防禦(PSS + seccomp + AppArmor)的互補關係與啟用順序

基礎知識:Linux 容器安全機制

容器逃逸的防禦建立在 Linux kernel 的多層安全機制之上。理解這些機制,才能明白後面的防禦配置為什麼有效。

Linux Capabilities

傳統 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(Secure Computing Mode)

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——阻止重啟或載入新 kernel
  • add_key / keyctl——阻止操作 kernel keyring
  • ptrace——阻止追蹤其他 process(擋 S17)
  • unshare——阻止建立新 namespace

AppArmor

AppArmor 是 Linux 的 MAC(Mandatory Access Control,強制存取控制)機制,和傳統的 DAC(Discretionary Access Control,自主存取控制)互補:

  • DAC:檔案擁有者決定誰能存取(chmod、chown)→ root 可以繞過
  • MAC:系統管理員定義強制規則 → 即使 root 也無法違反

AppArmor profile 有三種模式:

模式 行為 用途
Enforce 違反規則的操作被拒絕並記錄 生產環境
Complain 違反規則的操作被允許但記錄 測試階段
Unconfined 不限制(預設) 未配置 AppArmor

AppArmor profile 可限制四類操作:檔案讀寫(路徑 + 權限)、網路存取(協議 + 埠)、Capability 使用、程式執行(哪些二進位可以 exec)。

Pod Security Standards(PSS)

PSS 是 K8s 內建的 Pod 安全分級制度,定義三個安全等級:

等級 設計理念 適用場景
Privileged 完全不限制 系統元件(kube-proxy、CNI)
Baseline 禁止已知的高風險配置(hostNetwork、privileged) 一般工作負載的最低要求
Restricted 在 Baseline 之上進一步限制(必須 drop ALL caps、只能用特定 volume types) 安全敏感的工作負載

為什麼是三級而非兩級?因為某些系統元件(如 CNI plugin、日誌收集器)確實需要 host 權限,直接二分法(全開/全關)會讓這些元件無法運行。三級制讓管理員可以對不同 Namespace 套用不同等級。

安全檢查順序

當容器內的程式發出一個 syscall 時,kernel 的檢查順序:

程式發出 syscall(例如 mount())

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 建立請求


Pod Security Standards (PSS)

什麼是 PSS

Pod Security Standards 是 Kubernetes 1.25 起 GA 的內建安全策略機制,取代了已廢棄的 PodSecurityPolicy(PSP)。PSS 透過 Namespace label 控制,不需要安裝任何額外元件。

三個層級比較

層級 限制 阻擋的逃逸 適用場景
Privileged 無限制 無 kube-system 等系統 NS
Baseline 阻擋已知危險配置 S13, S14, S17, S18 一般應用
Restricted 最嚴格限制 全部六式 生產環境

Baseline vs 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 層級的具體限制

# 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

在 Namespace 上啟用 PSS

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 的版本鎖定

# 可以鎖定 PSS 版本,避免升級 K8s 時規則變更影響應用
kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=v1.30

PSS vs Kyverno

項目 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 Profile

什麼是 seccomp

seccomp(Secure Computing Mode)限制 process 可以使用的系統呼叫。Linux 有 400+ 個 syscall,但大部分容器只需要其中 50–100 個。seccomp profile 允許你明確定義白名單。

seccomp 的三種模式

模式 說明 效果
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

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 是最重要的防禦。

自訂 seccomp profile

{
  "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 即使在非特權容器中也會被阻擋。

seccomp 可以阻擋的逃逸

逃逸 使用的 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) 困難

在 Pod 中套用 seccomp

# 方法 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。

使用 OCI hook 記錄 seccomp 違規

# 在 strace 模式下觀察哪些 syscall 被阻擋
$ strace -c -f <command> 2>&1 | grep EPERM
# 可以看到哪些 syscall 因為 seccomp 被拒絕

AppArmor Profile

什麼是 AppArmor

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 系)

KOAD 的 AppArmor profile

#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,
}

載入和套用 AppArmor

# 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 可以阻擋的逃逸

逃逸 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 vs SELinux

項目 AppArmor SELinux
預設發行版 Ubuntu, SUSE RHEL, CentOS, Fedora
控制模型 路徑為主 標籤為主
學習曲線 低 高
細粒度 中 高
K8s 支援 1.4+ (beta) → 1.30 GA 支援但更複雜

Capabilities 管理

Drop ALL + 最小集合

securityContext:
  capabilities:
    drop: ["ALL"]          # 先移除全部
    add: ["NET_BIND_SERVICE"]  # 只加回需要的(綁定 <1024 port)

常見應用需要的最小 Capabilities

應用類型 需要的 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

逃逸 需要的 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

三者的互補關係

逃逸路徑 PSS seccomp AppArmor


攻防驗證實測:重放攻擊驗證防禦

理論講完了,現在動手驗證——用 Day 8–11 的逃逸手法重新攻擊,確認防禦真的有效。

Step 1:啟用 PSS Restricted

# 主機終端 — 在 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

Step 2:重放 S13 Privileged mount 逃逸(Day 08)

# 主機終端 — 嘗試重新建立特權 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 逃逸無法啟動。

Step 3:重放 S17 SYS_PTRACE 注入(Day 11)

# 主機終端 — 嘗試建立需要 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 逃逸的兩個前提都被封鎖。

Step 4:重放 S15 Docker.sock 逃逸(Day 10)

# 主機終端 — 嘗試建立掛載 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 會擋的關鍵差異。

Step 5:驗證 seccomp 阻擋效果

# 主機終端 — 建立一個套用 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。這是第二層防禦。

Step 6:清理測試 Pod

# 主機終端 — 清理驗證用的 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。兩層防禦互相補位——即使某一層被繞過,另一層仍然有效。


CKS 考點

考點 本日內容 權重
Pod Security Standards Restricted 層級設定 Minimize Microservice 20%
seccomp Profile 建立和套用 System Hardening 10%
AppArmor Profile 載入和套用 System Hardening 10%
Capabilities drop ALL + add 最小集 Minimize Microservice 20%

CKS 模擬題

題目:為 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

本日小結

你完成了什麼

  • [x] 理解 Linux Capabilities、seccomp、AppArmor 三層安全機制
  • [x] 設定 PSS Restricted 層級(warn → enforce 路徑)
  • [x] 撰寫自訂 seccomp profile 阻擋 mount / ptrace / mknod syscall
  • [x] 套用 AppArmor profile 到 Pod(RuntimeDefault 或自訂)
  • [x] 驗證防禦策略能阻擋 Day 8–11 的六種逃逸手法

完成度自我檢查

  • [ ] 已設定 PSS Restricted 層級(先 warn 再 enforce)並驗證阻擋特權容器
  • [ ] 已撰寫自訂 seccomp profile 並確認阻擋 mount / ptrace / mknod syscall
  • [ ] 已套用 AppArmor profile 到 Pod 並驗證其限制效果
  • [ ] 能驗證 PSS Restricted 阻擋 S13(Privileged mount)場景
  • [ ] 能驗證 seccomp 阻擋 S17(SYS_PTRACE)場景
  • [ ] 能解釋 PSS、seccomp、AppArmor 三層防禦的互補關係和建議啟用順序

關鍵帶走

  1. PSS Restricted:Admission 層面阻擋 privileged、hostPID、hostPath
  2. seccomp:syscall 層面限制 ptrace、mknod 等危險呼叫
  3. AppArmor:檔案和 capability 層面精確控制存取範圍

三者的防禦範圍互補,不是擇一使用。建議的啟用順序:

  1. 先啟用 PSS Restricted(warn + audit)→ 觀察 → enforce
  2. 再啟用 seccomp RuntimeDefault(最小改動)
  3. 最後考慮自訂 AppArmor profile(需要更多部署工作)

下一步

明天 Day 13 防禦逃逸(下)——gVisor 沙箱和容器不變性,用隔離和最小化進一步縮小攻擊面。



上一篇
Day 11|逃逸第六式 + 核心 CVE:SYS_PTRACE 注入 + Dirty Pipe
系列文
資安這條路:從攻擊者視角看 Kubernetes 共 12 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言