Kubernetes 的安全性和合規性日益重要,OPA 提供了強大的策略引擎,能有效控制叢集資源的存取和操作。透過 AdmissionReview 物件,OPA 能夠評估 API 伺服器的請求,確保只有符合策略的請求才能被執行。本文將詳細介紹如何整合 OPA 與 Kubernetes,實作驗證和變更兩種准入控制,並探討如何編寫 Rego 策略、組態入口點以及使用自定義函式庫,以提升策略管理效率和靈活性。此外,也將介紹 Styra DAS 集中式管理方案,簡化 OPA 資源的管理和監控。文章最後,將探討 OPA 與 Kubernetes 整合的最佳實務、注意事項以及未來發展方向,協助讀者更深入理解 OPA 在 Kubernetes 環境中的應用價值。
OPA 驗證策略與 Kubernetes 整合實務
驗證策略的核心概念
OPA(Open Policy Agent)驗證策略是 Kubernetes 環境中的重要安全機制,用於控制和管理叢集資源的存取與操作。驗證策略透過 AdmissionReview 物件來評估 API 伺服器的請求,確保叢集的安全性和合規性。
策略匹配與評估流程
驗證策略首先需要匹配到特定的請求型別,例如建立或更新 Pod。匹配成功後,策略會進一步評估請求的有效性。以範例程式碼來說,策略會檢查容器映像是否來源於允許的登入檔:
deny[msg] {
req_kind = "Pod"
req_op in allowed_ops
image = pod_containers[_].image
not reg_matches_any(image, valid_registries)
msg = sprintf("POD_INVALID: %q image is not sourced from an authorized registry. Valid registries are %q. Resource ID (ns/name/kind): %q", [image, allowed_regs, req_id])
}
內容解密:
deny[msg]
定義了一個拒絕規則,當條件滿足時傳回錯誤訊息。req_kind = "Pod"
確保該策略只適用於 Pod 型別的資源。req_op in allowed_ops
檢查請求的操作是否為允許的操作(建立或更新)。image = pod_containers[_].image
取得所有容器映像的名稱。not reg_matches_any(image, valid_registries)
檢查映像是否來源於允許的登入檔。msg
構建了詳細的錯誤訊息,包括無效映像、允許的登入檔和資源 ID。
OPA 策略入口點的重要性
OPA 需要正確組態策略入口點才能作為 Kubernetes 的動態 Webhook 服務運作。缺少正確的入口點組態將導致叢集操作失敗,例如無法建立 Pod 或檢視日誌:
Error from server (InternalError): Internal error occurred: Authorization error (user=kube-apiserver-kubelet-client, verb=get, resource=nodes, subresource=proxy)
主要入口點策略組態
正確的入口點策略必須生成符合 AdmissionReview 合約的回應:
main = {
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": response,
}
response = {
"allowed": false,
"uid": uid,
"status": {
"reason": reason,
},
}
內容解密:
main
定義了 OPA 的主要入口點,傳回一個 AdmissionReview 物件。response
包含了評估結果,包括是否允許請求、請求 UID 和狀態原因。allowed
欄位指示請求是否被允許。uid
必須與請求中的 UID 相匹配。status
物件提供了驗證失敗的詳細原因。
策略評估的實際應用
當 OPA 評估一個無效的請求時,會傳回詳細的錯誤訊息給客戶端:
Error from server (POD_INVALID: "public.ecr.aws/eks-distro/kubernetes/pause:3.2" image is not sourced from an authorized registry. Valid registries are ["GOOD_REGISTRY", "VERY_GOOD_REGISTRY"]. Resource ID (ns/name/kind): "opa-test/test-pod/Pod"): error when creating "tests/99-test-pod.yaml"
圖表說明:OPA 驗證流程
graph LR A[Kubernetes API 請求] --> B[OPA Webhook] B --> C{匹配驗證策略} C -->|匹配成功| D[評估請求有效性] C -->|匹配失敗| E[放行請求] D -->|驗證透過| F[允許請求] D -->|驗證失敗| G[傳回錯誤訊息]
圖表翻譯: 此圖示展示了 OPA 在 Kubernetes 環境中的驗證流程:
- Kubernetes API 請求首先到達 OPA Webhook。
- OPA 嘗試匹配適當的驗證策略。
- 如果匹配成功,則進行請求有效性評估。
- 根據評估結果決定是否允許或拒絕請求。
最佳實踐與注意事項
策略設計
- 確保策略邏輯清晰且易於維護
- 使用 Rego 語言的進階特性提高策略效率
效能最佳化
- 最小化策略匹配範圍
- 使用快取機制提高評估效能
安全性考量
- 定期更新和審查策略
- 監控 OPA 日誌以發現潛在問題
更智慧的策略管理
- 結合機器學習技術實作動態策略調整
- 自動化策略最佳化
跨叢集策略統一管理
- 研究多叢集環境下的 OPA 佈署方案
- 統一管理不同環境下的安全策略
與 DevOps 流程整合
- 將 OPA 納入 CI/CD 管道
- 實作自動化的策略測試和驗證
透過持續最佳化和創新,OPA 將在未來的雲原生安全領域扮演更加關鍵的角色。
使用Open Policy Agent(OPA)實作Kubernetes的准入控制
前言
在現代的雲原生環境中,Kubernetes已經成為容器協調的標準。然而,隨著叢集規模的擴大和複雜度的增加,如何有效地管理和控制叢集中的資源變得越來越重要。Open Policy Agent(OPA)是一種開源的通用策略引擎,可以與Kubernetes整合,實作對叢集資源的准入控制。本文將探討如何使用OPA實作Kubernetes的准入控制,包括驗證和變更准入控制。
OPA與Kubernetes的整合
OPA可以透過Kubernetes的准入控制器整合到叢集中。准入控制器是一種用於攔截和處理Kubernetes API請求的元件。OPA可以作為一個驗證或變更准入Webhook,攔截和處理建立、更新和刪除等請求。
安裝OPA
首先,需要在Kubernetes叢集中安裝OPA。可以使用Helm chart或YAML檔案進行安裝。以下是使用YAML檔案安裝OPA的範例:
apiVersion: v1
kind: Namespace
metadata:
name: opa
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: opa
namespace: opa
spec:
replicas: 1
selector:
matchLabels:
app: opa
template:
metadata:
labels:
app: opa
spec:
containers:
- name: opa
image: openpolicyagent/opa:latest
args:
- "run"
- "--server"
- "--log-level=debug"
- "--config-file=/etc/opa/config.yaml"
volumeMounts:
- name: config
mountPath: /etc/opa
readOnly: true
volumes:
- name: config
configMap:
name: opa-config
組態OPA
安裝完成後,需要組態OPA以使其能夠與Kubernetes整合。需要建立一個ConfigMap來組態OPA:
apiVersion: v1
kind: ConfigMap
metadata:
name: opa-config
namespace: opa
data:
config.yaml: |
services:
kube:
url: https://kubernetes.default.svc:443
labels:
app: opa
plugins:
kube-mgmt:
enabled: true
驗證准入控制
驗證准入控制是一種用於檢查Kubernetes資源是否符合特定策略的機制。OPA可以使用Rego語言編寫策略,並將其載入到叢集中。
編寫Rego策略
以下是一個簡單的Rego策略,用於檢查Deployment資源是否來自授權的倉函式庫:
package kubernetes.admission
import data.lib.k8s.helpers as helpers
deny[msg] {
helpers.request_kind == "Deployment"
helpers.allowed_operations[helpers.request_operation]
image = helpers.deployment_containers[_].image
not reg_matches_any(image, valid_deployment_registries_v2)
msg = sprintf("%q: %q image is not sourced from an authorized registry. Resource ID (ns/name/kind): %q", [helpers.deployment_error, image, helpers.request_id])
}
valid_deployment_registries_v2 = {registry |
allowed = "GOOD_REGISTRY"
registries = split(allowed, ",")
registry = registries[_]
}
reg_matches_any(str, patterns) {
reg_matches(str, patterns[_])
}
reg_matches(str, pattern) {
contains(str, pattern)
}
載入Rego策略
需要將Rego策略載入到OPA中,可以使用ConfigMap來實作:
apiVersion: v1
kind: ConfigMap
metadata:
name: deployment-registry-allowed
namespace: opa
data:
main: |
package kubernetes.admission
# Rego policy content here...
自定義Helper函式庫
為了簡化Rego策略的編寫,可以建立自定義的Helper函式庫。以下是一個範例:
package lib.k8s.helpers
allowed_operations = allowed_ops {
allowed_ops := {"CREATE", "UPDATE"}
}
request_operation = op {
op := input.request.operation
}
request_metadata_labels = labels {
labels := input.request.object.metadata.labels
}
# Other helper functions...
使用Helper函式庫
可以在Rego策略中匯入Helper函式庫,以簡化程式碼:
import data.lib.k8s.helpers as helpers
deny[msg] {
helpers.request_kind == "Deployment"
# Use helper functions here...
}
變更准入控制
變更准入控制是一種用於修改Kubernetes資源的機制。OPA可以組態為變更准入Webhook,以修改資源。
組態變更准入控制
需要建立一個MutatingWebhookConfiguration資源,以組態OPA為變更准入Webhook:
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: opa-mutating-webhook
webhooks:
- admissionReviewVersions:
- v1
clientConfig:
service:
name: opa
namespace: opa
port: 443
failurePolicy: Fail
matchPolicy: Equivalent
name: mutating-webhook.openpolicyagent.org
namespaceSelector:
matchExpressions:
- key: openpolicyagent.org/webhook
operator: NotIn
values:
- ignore
rules:
- apiGroups:
- '*'
apiVersions:
- '*'
operations:
- CREATE
- UPDATE
resources:
- pods
scope: '*'
sideEffects: None
timeoutSeconds: 10
圖表翻譯:
此圖示展示了OPA與Kubernetes整合的架構圖。
graph LR; A[Kubernetes API] -->|請求|> B[OPA]; B -->|驗證或變更|> C[Kubernetes 資源]; C -->|結果|> B; B -->|回應|> A;
圖表翻譯: 此圖表呈現了 Kubernetes API 請求經過 OPA 處理後傳回結果給 Kubernetes 資源並最終回應給 Kubernetes API 的流程。整個過程中,OPA 負責驗證或變更請求,最終由 Kubernetes 資源處理並傳回結果給 OPA,再由 OPA 回應給 Kubernetes API。
隨著雲原生技術的不斷發展,Kubernetes和OPA的整合將會變得越來越重要。未來,我們可以期待看到更多根據OPA的創新應用,例如更複雜的策略管理和更強大的准入控制功能。同時,隨著社群的不斷貢獻和改進,OPA的功能和效能也將會不斷提升。
組態規則以處理 Kubernetes 資源型別
再次強調,准入 Webhook 只會在 CREATE 或 UPDATE 操作期間影響 Pods,並且只會在未標記 openpolicyagent.org/webhook=ignore
的 Namespaces 中生效。對傳入資源(包含在 API 伺服器請求中)的變更是使用 RFC 6902 中定義的 JSON patch 架構進行的。
正如我在本文前面提到的,OPA 社群非常龐大,GitHub 上的 OPA 組織至少有 22 個倉函式庫。為了展示變更准入的工作原理,我使用了舊的 GitHub 專案 OPA Library 中的一部分。我透過 ConfigMap 安裝了 kubernetes/mutating-admission/main.rego
政策,以建立入口點政策,該政策建立了 Kubernetes AdmissionReview.response 物件的合約欄位。由於 Rego 套件不是系統套件,我決定更改 OPA 伺服器的 default_decision
引數,如下所示;另一個選擇是更改套件到系統:
# OPA 准入控制器伺服器引數
args:
- "run"
- "--server"
- "--tls-cert-file=/certs/tls.crt"
- "--tls-private-key-file=/certs/tls.key"
- "--addr=0.0.0.0:8443"
- "--addr=http://127.0.0.1:8181"
# - "--set=bundles.default.resource=bundle.tar.gz"
- "--log-format=json"
- "--set=status.console=true"
- "--set=decision_logs.console=true"
- "--set=default_decision=/library/kubernetes/admission/mutating/main"
內容解密:
此組態更改了 OPA 伺服器的預設決策路徑,以匹配入口點政策的位置。這使得 OPA 能夠正確處理變更准入請求。
另一個匹配選項是不修改 OPA 伺服器的 default_decision
引數,而是向 Webhook 組態新增 path
欄位,如下所示:
# 變更 Webhook 服務組態
service:
namespace: opa
name: opa
path: /v0/data/library/kubernetes/admission/mutating/main
port: 443
內容解密:
此組態透過指定正確的決策路徑,使 Kubernetes API 伺服器能夠正確地將准入請求傳送到 OPA。
在第 2 章中,我向您介紹了 OPA REST API 的 /v1/...
端點。當使用 post 查詢時,v1 API 端點需要一個定義的輸入 JSON 檔案,如下所示:
// 示例輸入檔案
{
"input": {
"message": "world"
}
}
當 Kubernetes API 伺服器將 AdmissionReview 檔案釋出到 OPA 以進行變更或驗證決策時,它不使用輸入檔案結構。v0 API 端點不需要這種結構;它處理原始輸入,隨後的規則處理 POST 請求。
由於 OPA 的預設決策路徑是 /system/main
,我需要更新入口點套件和主規則以匹配預設的 OPA 決策路徑,或者更新 default_decision
引數以匹配入口點套件和主規則,或者更新 Webhook service.path
欄位以匹配入口點套件和主規則。
如果沒有這個匹配,我將在 OPA 決策日誌中看到以下錯誤:
# 主入口點政策不匹配錯誤
{
"decision_id": "1739aacc-48e3-4346-a19a-6ba479dd6b26",
"error": {
"code": "undefined_document",
"message": "document missing: data.system.main"
},
...
}
內容解密:
此錯誤表示 OPA 無法找到與預設決策路徑匹配的政策。需要更新組態以確保正確的匹配。
接下來,我使用了上述 OPA Library 專案中的政策模式來修補 Pods,如果標籤不存在。我的政策使用了入口點 Rego 中包含的多個輔助函式。為了使用它們,我在與入口點 Rego 同一個套件中定義了我的政策 Rego:
# Configmap with mutating policy
kind: ConfigMap
apiVersion: v1
metadata:
name: label-pods
namespace: opa
labels:
openpolicyagent.org/policy: rego
data:
main: |
package system
############################################################
# PATCH rules
# Note: All patch rules should start with `isValidRequest` and
# `isCreateOrUpdate`
############################################################
# add billing,env,owner labels to pods
patch[patchCode] {
isValidRequest
isCreateOrUpdate
input.request.kind.kind == "Pod"
not hasLabelValue(input.request.object, "billing", "lob-cc")
patchCode = makeLabelPatch("add", "billing", "lob-cc", "")
}
patch[patchCode] {
isValidRequest
isCreateOrUpdate
input.request.kind.kind == "Pod"
not hasLabelValue(input.request.object, "env", "dev")
patchCode = makeLabelPatch("add", "env", "dev", "")
}
patch[patchCode] {
isValidRequest
isCreateOrUpdate
input.request.kind.kind == "Pod"
not hasLabelValue(input.request.object, "owner", "jimmy")
patchCode = makeLabelPatch("add", "owner", "jimmy", "")
}
內容解密:
此 Rego 程式碼定義了三個修補規則,用於為 Pods 新增 billing
、env
和 owner
標籤,如果這些標籤不存在。
變更過程使用入口點 Rego 中的函式來建立一個扁平化的修補陣列,將陣列編碼為 JSON,然後將修補內容 Base64 編碼,以便傳送回 Kubernetes API 伺服器:
# marshaling JSON from patch array, then base64 encoding
x := {
"allowed": true,
"uid": response_uid,
"patchType": "JSONPatch",
"patch": base64.encode(json.marshal(fullPatches)),
}
內容解密:
此程式碼將修補陣列編碼為 JSON,然後進行 Base64 編碼,以便在 AdmissionReview 回應中傳回 Kubernetes API 伺服器。
如果我們從 OPA 取得決策日誌,並解碼 Base64 編碼的字串,我們可以看到應用於 Pod 請求的所有三個修補:
// AdmissionReview 回應傳回到 API 伺服器
..."result": {
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"allowed": true,
"patch": "W3sib...fV0=",
"patchType": "JSONPatch",
"uid": "32c8e37c-8f01-4787-bf47-58e70d07fed1"
}
},...
# base64 decoded JSON patches
$ echo "W3si...fV0=" | base64 -d
[
{"op":"add","path":"/metadata/labels","value":{}},
{"op":"add","path":"/metadata/labels/billing","value":"lob-cc"},
{"op":"add","path":"/metadata/labels/env","value":"dev"},
{"op":"add","path":"/metadata/labels/owner","value":"jimmy"}
]
內容解密:
此輸出顯示了應用於 Pod 請求的三個 JSON 修補物件。
既然我們已經探討瞭如何使用 OPA 的驗證和變更 Webhook,讓我們來看看如何集中管理 OPA 資源。
使用 Styra DAS 的集中式 OPA 管理
在第 3 章中,我們探討了使用 Styra DAS 中央管理 OPA Agents 和政策包。事實上,使用 OPA(經典版)進行 Kubernetes 的驗證和變更 Webhook,使得您能夠使用 OPA 管理功能,例如包和決策日誌。
要在本地 minikube 環境中使用 Styra DAS,我在 Styra DAS(免費版)工作區中建立了一個系統。然後,我使用組態的 Styra 系統中的安裝命令,在本地叢集中建立 Styra 資源。
graph LR; A[建立 Styra DAS 系統] --> B[生成安裝命令]; B --> C[在本地叢集中執行安裝命令]; C --> D[建立 Styra 資源];
圖表翻譯: 此圖表顯示了在本地 minikube 環境中使用 Styra DAS 的步驟。首先,在 Styra DAS 工作區中建立一個系統,然後生成安裝命令,最後在本地叢集中執行這些命令以建立 Styra 資源。
圖表說明:
此圖表清晰地展示了使用 Styra DAS 的集中式 OPA 管理流程,從建立系統到在本地叢集中佈署資源的整個過程。這種方法簡化了 OPA 資源的管理,並提供了集中式的控制和監控能力。