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

為所有工作負載啟用 seccomp 的 v1.22 alpha 新功能

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

您可以使用 Pod 及其容器的 securityContext 欄位來調整工作負載的安全性相關組態。Kubernetes 在此 SecurityContext 中引入了專用的 seccomp 相關 API 欄位,並在 v1.19.0 中 seccomp 升級到正式發行 (GA)。此增強功能讓您可以更輕鬆地指定整個 Pod 或特定容器是否應以以下身分執行:

  • Unconfined:不會啟用 seccomp
  • RuntimeDefault:將使用容器執行階段預設設定檔
  • Localhost:將套用節點本機設定檔,該設定檔由 kubelet 的 seccomp 設定檔根目錄 (<kubelet-root-dir>/seccomp) 的相對路徑引用

隨著 seccomp 的升級,從整體安全性的角度來看,沒有任何改變,因為 Unconfined 仍然是預設值。如果您從 Kubernetes 版本的升級路徑和回溯相容性角度來看,這完全沒有問題。但這也意味著工作負載更有可能在完全沒有 seccomp 的情況下執行,這應該從長遠來看加以修正。

SeccompDefault 來救援

Kubernetes v1.22.0 引入了一個新的 kubelet 功能閘道 SeccompDefault,它已以 alpha 狀態新增,就像其他所有新功能一樣。這表示預設情況下它是停用的,並且可以為每個 Kubernetes 節點手動啟用。

此功能有什麼作用?嗯,它只是將預設 seccomp 設定檔從 Unconfined 變更為 RuntimeDefault。如果 Pod 資訊清單中沒有另行指定,則此功能將透過使用容器執行階段的預設設定檔來增加一組更高的安全性限制。這些設定檔在執行階段之間可能會有所不同,例如 CRI-Ocontainerd。它們也會因其使用的硬體架構而異。但一般來說,這些預設設定檔允許常見數量的系統呼叫,同時封鎖更危險的系統呼叫,這些系統呼叫不太可能或不安全在容器化應用程式中使用。

啟用此功能

必須進行兩項 kubelet 組態變更才能啟用此功能

  1. 啟用功能閘道,方法是透過命令列 (--feature-gates) 或 kubelet 組態 檔案設定 SeccompDefault=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。因此,如果 SeccompProfile 欄位在 SecurityContext 中未設定,則無法透過 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 版本。監控應用程式主機稽核日誌(預設為 /var/log/audit/audit.log)或系統日誌項目(預設為 /var/log/syslog),以透過 type=SECCOMP(適用於稽核)或 type=1326(適用於系統日誌)尋找系統呼叫。將系統呼叫 ID 與 Linux Kernel 原始碼中列出的系統呼叫 ID 進行比較,並將它們新增至自訂設定檔。請注意,自訂稽核政策可能會導致遺失系統呼叫,具體取決於 auditd 的組態。

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

部署修改後的應用程式

根據應用程式測試的結果,可能需要變更應用程式部署,方法是指定 Unconfined 或自訂 seccomp 設定檔。如果應用程式在 RuntimeDefault 的情況下按預期運作,則不需要這樣做。

啟用 kubelet 組態

如果一切順利,則此功能已準備好透過 kubelet 組態或其對應的 CLI 旗標啟用。應在每個節點上執行此操作,以降低在執行應用程式測試時遺失系統呼叫的整體風險。如果可以在叢集內監控稽核日誌,則建議這樣做以處理最終遺失的 seccomp 事件。如果應用程式按預期運作,則可以為叢集內的其他節點啟用此功能。

結論

感謝您閱讀這篇網誌文章!希望您和我一樣喜歡看到 seccomp 設定檔的使用方式在過去的 Kubernetes 版本中如何演變。在您自己的叢集上,將預設 seccomp 設定檔變更為 RuntimeDefault(使用此新功能)並查看安全性優勢,當然,隨時歡迎您聯繫以提供意見或提出問題。


編輯註記:如果您對這篇網誌文章有任何問題或意見反應,請隨時透過 Kubernetes Slack 中的 #sig-node 聯繫我們。