本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

使用新的 v1.22 Alpha 特性為所有工作負載啟用 seccomp

這篇部落格文章介紹了一個在 v1.22 中引入的新 Kubernetes 功能,它在現有 seccomp 支援的基礎上增加了一個額外的安全層。Seccomp 是一種 Linux 程序的安全機制,用於根據一組定義的規則過濾系統呼叫(syscalls)。將 seccomp 配置檔案應用於容器化工作負載是增強應用程式部署安全性的關鍵任務之一。開發人員、站點可靠性工程師和基礎設施管理員必須攜手合作,在應用程式生命週期中建立、分發和維護這些配置檔案。

您可以使用 Pod 及其容器的 securityContext 欄位來調整工作負載的安全相關配置。Kubernetes 在 v1.19.0 中將 seccomp 晉級為通用可用性 (GA) 後,在這個 SecurityContext 中引入了專用的 seccomp 相關 API 欄位。這項增強功能使得更容易指定整個 Pod 或特定容器應以下列方式執行:

  • Unconfined:seccomp 將不被啟用
  • RuntimeDefault:將使用容器執行時的預設配置檔案
  • Localhost:將應用一個節點本地配置檔案,該檔案透過相對於 kubelet 的 seccomp 配置檔案根目錄 (<kubelet-root-dir>/seccomp) 的路徑進行引用

隨著 seccomp 的畢業,從整體安全形度來看並沒有改變,因為 Unconfined 仍然是預設設定。如果您從 Kubernetes 版本的升級路徑和向後相容性角度考慮,這完全沒問題。但這也意味著工作負載很可能根本沒有執行 seccomp,這種情況應該長期解決。

SeccompDefault 來救援

Kubernetes v1.22.0 引入了一個新的 kubelet 功能門 (feature gate) SeccompDefault,它像其他所有新功能一樣以 alpha 狀態新增。這意味著它預設是停用的,並且可以為每個 Kubernetes 節點手動啟用。

這個功能有什麼作用?它只是將預設的 seccomp 配置檔案從 Unconfined 更改為 RuntimeDefault。如果在 Pod 清單中沒有另外指定,那麼該功能將透過使用容器執行時的預設配置檔案來新增一組更高的安全約束。這些配置檔案可能因 CRI-Ocontainerd 等執行時而異。它們也因所使用的硬體架構而異。但總的來說,這些預設配置檔案允許常用數量的系統呼叫,同時阻止那些在容器化應用程式中不太可能或不安全使用的更危險的系統呼叫。

啟用該特性

要啟用該功能,需要進行兩個 kubelet 配置更改

  1. 啟用功能門:透過命令列 (--feature-gates) 或 kubelet 配置檔案SeccompDefault=true 設定為 true。
  2. 開啟功能:透過新增 --seccomp-default 命令列標誌或透過 kubelet 配置檔案 (seccompDefault: true) 開啟功能。

如果只完成了上述步驟之一,kubelet 在啟動時將報錯。

試一試

如果在節點上啟用了該功能,則可以像這樣建立一個新的工作負載

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: test-container
      image: nginx:1.21

現在可以透過使用 crictl 並檢查容器的 執行時規範 來檢查所使用的 seccomp 配置檔案

CONTAINER_ID=$(sudo crictl ps -q --name=test-container)
sudo crictl inspect $CONTAINER_ID | jq .info.runtimeSpec.linux.seccomp
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"],
  "syscalls": [
    {
      "names": ["_llseek", "_newselect", "accept", …, "write", "writev"],
      "action": "SCMP_ACT_ALLOW"
    },
    
  ]
}

您可以看到,底層容器執行時(在本例中為 CRI-Orunc)成功應用了預設的 seccomp 配置檔案。此配置檔案預設拒絕所有系統呼叫,同時允許常用系統呼叫,例如 acceptwrite

請注意,目前該功能不會影響任何 Kubernetes API。因此,如果 SecurityContext 中的 SeccompProfile 欄位未設定,則無法透過 kubectl getdescribe 檢索所使用的 seccomp 配置檔案。

該功能在使用 Pod 中的多個容器時也適用,例如,如果您像這樣建立一個 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: test-container-nginx
      image: nginx:1.21
      securityContext:
        seccompProfile:
          type: Unconfined
    - name: test-container-redis
      image: redis:6.2

那麼您應該會看到 test-container-nginx 在沒有 seccomp 配置檔案的情況下執行

sudo crictl inspect $(sudo crictl ps -q --name=test-container-nginx) |
    jq '.info.runtimeSpec.linux.seccomp == null'
true

而容器 test-container-redis 則使用 RuntimeDefault 執行

sudo crictl inspect $(sudo crictl ps -q --name=test-container-redis) |
    jq '.info.runtimeSpec.linux.seccomp != null'
true

Pod 本身也同樣適用,它也使用預設配置檔案執行

sudo crictl inspectp (sudo crictl pods -q --name test-pod) |
    jq '.info.runtimeSpec.linux.seccomp != null'
true

升級策略

建議分多個步驟啟用該功能,每個步驟都存在不同的風險和緩解措施。

功能門啟用

在 kubelet 層面啟用功能門並不會開啟該功能,但可以透過使用 SeccompDefault kubelet 配置或 --seccomp-default CLI 標誌來使其成為可能。這可以由管理員針對整個叢集或僅一組節點來完成。

測試應用程式

如果您在專用測試環境中嘗試此操作,在節點上啟用此功能之前,您必須確保應用程式程式碼不會觸發 RuntimeDefault 配置檔案阻止的系統呼叫。這可以透過以下方式完成:

  • 推薦:分析程式碼(手動或透過使用 strace 執行應用程式),查詢可能被預設配置檔案阻止的任何已執行的系統呼叫。如果存在這種情況,您可以透過明確將 Pod 或容器設定為以 Unconfined 方式執行來覆蓋預設設定。或者,您可以建立一個自定義 seccomp 配置檔案(參見下面的可選步驟),該配置檔案基於預設配置檔案,透過將額外的系統呼叫新增到 "action": "SCMP_ACT_ALLOW" 部分。

  • 推薦:手動將配置檔案設定為目標工作負載,並使用滾動升級將其部署到生產環境。如果應用程式未能按預期工作,則回滾部署。

  • 可選:針對應用程式執行端到端測試套件,以在啟用 RuntimeDefault 的情況下觸發所有相關程式碼路徑。如果測試失敗,請使用上述相同的緩解措施。

  • 可選:基於預設配置檔案建立一個自定義 seccomp 配置檔案,並將其預設操作從 SCMP_ACT_ERRNO 更改為 SCMP_ACT_LOG。這意味著 seccomp 過濾器對未知系統呼叫將完全不影響應用程式,但系統日誌將指示哪些系統呼叫可能被阻止。這至少需要 Kernel 版本 4.14 以及最近的 runc 版本。透過 type=SECCOMP(用於審計)或 type=1326(用於 syslog)監控應用程式主機的審計日誌(預設為 /var/log/audit/audit.log)或 syslog 條目(預設為 /var/log/syslog)以獲取系統呼叫。將系統呼叫 ID 與 Linux 核心原始碼中列出的 進行比較,並將它們新增到自定義配置檔案中。請注意,自定義審計策略可能會導致缺少系統呼叫,具體取決於 auditd 的配置。

  • 可選:使用叢集附加元件,例如 Security Profiles Operator,透過其 日誌豐富 功能或使用其 記錄功能 來分析應用程式的配置檔案。這使得上述手動日誌調查變得過時。

部署修改後的應用程式

根據應用程式測試的結果,可能需要透過指定 Unconfined 或自定義 seccomp 配置檔案來更改應用程式部署。如果應用程式能夠按預期與 RuntimeDefault 配合工作,則不需要這樣做。

啟用 kubelet 配置

如果一切順利,那麼該功能就可以透過 kubelet 配置或其對應的 CLI 標誌來啟用。這應該在每個節點的基礎上完成,以減少在執行應用程式測試時,調查過程中遺漏系統呼叫的總體風險。如果可以在叢集內監控審計日誌,那麼建議這樣做以防萬一錯過 seccomp 事件。如果應用程式按預期工作,那麼該功能可以在叢集內的其他節點上啟用。

結論

感謝您閱讀這篇部落格文章!我希望您和我一樣,享受看到 seccomp 配置檔案在 Kubernetes 過去版本中的演變。在您自己的叢集上,將預設 seccomp 配置檔案更改為 RuntimeDefault(使用此新功能),並檢視其帶來的安全優勢,當然,歡迎隨時提出反饋或問題。


編者注:如果您對這篇部落格文章有任何問題或反饋,請隨時透過 Kubernetes slack 中的 #sig-node 聯絡我們。