1. SecurityContext
SecurityContext 定義 Pod 或 Container 的權限和存取控制。
apiVersion: v1
kind: Pod
spec:
securityContext: # Pod 層級: 套用於所有容器
runAsUser: 1000 # 容器執行 UID
runAsGroup: 3000 # 主要 GID
fsGroup: 2000 # 掛載 volume 的 GID
runAsNonRoot: true # 禁止以 root 執行
containers:
- name: app
image: myapp
securityContext: # Container 層級: 覆蓋 Pod 層級
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true # 將根檔案系統設為唯讀
capabilities:
add: ["NET_BIND_SERVICE"] # 新增 capability
drop: ["ALL"] # 先 drop 全部,再新增需要的
| 設定項目 | 層級 | 效果 |
|---|---|---|
runAsUser | Pod/Container | 以指定 UID 執行程序 |
runAsNonRoot | Pod/Container | UID = 0(root)時拒絕執行 |
readOnlyRootFilesystem | Container | 將根檔案系統設為唯讀掛載 |
allowPrivilegeEscalation | Container | 阻擋權限提升(如 sudo) |
privileged | Container | 以特權模式執行(等同 host 上的 root) |
fsGroup | Pod | Volume 檔案的 GID(用於共享 volume 存取) |
考試重點: Container 層級的 securityContext 會覆蓋 Pod 層級的設定。Pod 設定
runAsUser: 1000,Container 設定runAsUser: 2000,該容器會以 UID 2000 執行。常考模式:kubectl exec pod -- id或whoami確認使用者。
2. Linux Capabilities
Capabilities 允許在不給予完整 root 權限的情況下授予特定權限。
# 常見範例:
NET_BIND_SERVICE — 綁定 1024 以下的埠號(如 port 80)
NET_ADMIN — 網路管理(ifconfig 等)
SYS_TIME — 修改系統時鐘
CHOWN — 變更檔案所有者
SETUID/SETGID — 變更使用者/群組 ID
securityContext:
capabilities:
drop: ["ALL"] # 最佳實踐: 先 drop 全部
add: ["NET_BIND_SERVICE"] # 只加回需要的
3. ServiceAccounts
Pod 使用 ServiceAccount 向 Kubernetes API 進行身份驗證。
# 建立 ServiceAccount
kubectl create serviceaccount my-sa
# 綁定 Role
kubectl create rolebinding my-binding \
--role=pod-reader \
--serviceaccount=default:my-sa
# 在 Pod 中指定 SA
spec:
serviceAccountName: my-sa # 使用指定的 SA
automountServiceAccountToken: false # 停用 SA token 自動掛載
# 預設行為: default SA 的 token 會掛載到 /var/run/secrets/kubernetes.io/serviceaccount/
# 容器可使用該 token 呼叫 K8s API
| 概念 | 說明 |
|---|---|
| 預設 SA | 每個 Namespace 都有內建的 default SA(最小權限) |
| Token 掛載 | 除非停用,token 會自動掛載到 Pod |
automountServiceAccountToken: false | 停用 token 掛載(安全最佳實踐) |
4. readOnlyRootFilesystem + emptyDir
# readOnlyRootFilesystem: true 時,應用無法寫入根檔案系統。
# 但應用可能需要寫入暫存檔案 → 使用 emptyDir volume:
spec:
containers:
- name: app
image: myapp
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp # 暫存檔案寫入此處
- name: cache
mountPath: /app/cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
5. 速查表
| 任務 | YAML / 指令 |
|---|---|
| 以非 root 執行容器 | securityContext: runAsNonRoot: true |
| 以指定 UID 執行 | securityContext: runAsUser: 1000 |
| 唯讀檔案系統 | securityContext: readOnlyRootFilesystem: true |
| Drop 所有 capabilities | capabilities: drop: ["ALL"] |
| 指定 ServiceAccount | spec: serviceAccountName: my-sa |
| 確認容器使用者 | kubectl exec pod -- whoami |
6. 練習題
Q1: Pod spec 的 securityContext.runAsUser 設定為 1000(Pod 層級)。Pod 中某個容器的 securityContext.runAsUser 設定為 2000。該容器以哪個 UID 執行?
- A) 0(root,Pod 層級覆蓋)
- B) 1000(Pod 層級優先)
- C) 2000(Container 層級覆蓋 Pod 層級) ✓
- D) 同時以兩個 UID 執行
解析: Container 層級的 securityContext 設定會覆蓋 Pod 層級。該容器以 UID 2000 執行。同一 Pod 中沒有設定 Container 層級 securityContext 的其他容器會繼承 Pod 層級的 UID 1000。
Q2: 應用容器需要綁定 port 80(1024 以下的特權埠號),但不應以 root 執行。如何設定?
- A) 設定 securityContext.privileged: true
- B) 設定 securityContext.runAsUser: 0
- C) Drop 所有 capabilities 後只加回 NET_BIND_SERVICE ✓
- D) 使用 NodePort Service 替代 port 80
解析: Linux capabilities 提供細粒度的權限授予。NET_BIND_SERVICE 允許不以 root 身份綁定 1024 以下的埠號。最佳實踐是先 drop 全部再新增需要的: capabilities: { drop: ["ALL"], add: ["NET_BIND_SERVICE"] }。
Q3: 一個 Pod 設定了 readOnlyRootFilesystem: true。應用嘗試寫入 /tmp 但失敗。最佳解決方案是什麼?
- A) 移除 readOnlyRootFilesystem: true
- B) 設定 securityContext.privileged: true
- C) 在 /tmp 掛載 emptyDir volume ✓
- D) 在 /tmp 掛載 ConfigMap
解析: readOnlyRootFilesystem 阻止對容器檔案系統的寫入,但 emptyDir volume 是另一個可寫入的掛載點。在 /tmp 掛載 emptyDir 後,根檔案系統保持唯讀,但應用可以寫入暫存檔案。