OPA-Gatekeeper實驗:對特定使用者的更新時間視窗做限制

M27149發表於2021-11-18

實驗目的

OPA-Gatekeeper可以在Kubernetes 中,通過策略來實現一些額外的管理、安全方面的限制,例如:限制特定使用者在 Namespace 中的行為許可權

本次實驗將在test名稱空間內對開發人員的更新操作提供時間視窗限制

說明:
在自建的k8s叢集實驗成功。
在rancher2.4.4中建立失敗,原因是rego語法解析錯誤,已經提交到官方論壇,尚未解決。

實現思路

為了方便管理,開發人員將使用統一使用者deploy,
然後在k8s中新增基於角色的訪問控制(RBAC),即使用role和rolebinding,限制deploy在test名稱空間內的資源許可權。
最後使用gatekeeper做時間視窗限制。

Namespace role user
test manager deploy

實現步驟

1 建立使用者

  1. 建立私鑰
openssl genrsa -out deploy.key 2048
  1. 建立csr(證書籤名請求)檔案
openssl req -new -key deploy.key -out deploy.csr -subj "/CN=deploy/O=MGM"
  1. 通過叢集的CA證書和deploy的csr檔案,來為deploy頒發證書

我的CA證書是在/etc/kubernetes/pki/目錄下

openssl x509 -req -in deploy.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out deploy.crt -days 3650
  1. 為使用者配置credentials認證環境和context上下文環境
kubectl config set-credentials deploy --client-certificate=deploy.crt --client-key=deploy.key
kubectl config get-users

kubectl config set-context deploy --cluster=kubernetes --namespace=test --user=deploy
kubectl config get-contexts
  1. 測試切換使用者環境
kubectl config use-context deploy
kubectl config use-context kubernetes-admin@kubernetes

2 建立角色

在RBAC中,角色有兩種——普通角色(Role)和叢集角色(ClusterRole),ClusterRole是特殊的Role:

  • Role屬於某個名稱空間,而ClusterRole屬於整個叢集,其中包括所有的名稱空間
  • ClusterRole能夠授予叢集範圍的許可權,比如node資源的管理,比如非資源型別的介面請求(如"/health"),比如可以請求全名稱空間的資源(通過指定 --all-namespaces)

這裡要在test名稱空間內建立一個許可權小的普通角色

kind: Role
metadata:
  namespace: test
  name: manager
rules:
- apiGroups: ["apps"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["*"]
kubectl apply -f role.yaml
kubectl get role -n test

以上涉及的api資源檢視方式:

kubectl api-resources|grep "apps/v1"
kubectl api-resources|grep " v1"

3 將前面的角色和使用者繫結

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: manager-binding
  namespace: test
subjects:
- kind: User
  name: deploy
roleRef:
  kind: Role
  name: manager
  apiGroup: "rbac.authorization.k8s.io"
kubectl apply -f Rolebinding.yaml
kubectl get rolebinding -n test

測試deploy使用者建立pod

kubectl get pods -n test  --context=deploy
kubectl run nginx --image=nginx:latest -n default  --context=deploy
kubectl run nginx --image=nginx:latest -n test  --context=deploy

4 安裝gatekeeper

wget https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml
kubectl apply -f gatekeeper.yaml

檢查

kubectl get ConstraintTemplate #等待出現 No resources found 才表示成功
kubectl get crd #CustomResourceDefinitions #只有當AGE不為<Invalid>而是一個時間時,才表示這個資源物件可用

5 ConstraintTemplate 約束模板

在定義約束之前,必須先定義一個 ConstraintTemplate (相當於創造了自定義的api-resources型別)
它主要內容描述的是 執行約束的Rego語句 和 約束的模式。

約束的模式允許admin對約束行為進行微調,就像對一個函式傳參。

spec由兩部分組成:
- crd:CustomResourceDefinition 自定義資源
- targets:一組target,是以 rego 程式碼塊為主體

模板參考

以下模板提供了時間視窗限制(每週一、三,晚23:30至次日06:00)

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: updatelimits
spec:
  crd:
    spec:
      names:
        kind: UpdateLimits
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package updatelimit

       ###TIME check
        nanosec := time.now_ns()
        wday := time.weekday(nanosec)
        dhour :=time.clock(nanosec)[0]
        dmin :=time.clock(nanosec)[1]

       ###userAuth Check
       #ns := input.review.object.metadata.namespace
        uname := input.review.userInfo.username


        # 23:30至次日06:00,要考慮 k8s 的8h時差
        timeOK {
          dhour==15
          dmin>=30
        }
        timeOK {
          dhour>15
          dhour<22
        }
        weekdayOK{
          wday == "Wednesday"
        }
        weekdayOK{
          wday == "Monday"
        }

        #三種不同情況下的限制提示

        violation[{"msg": msgX}] {
          uname != "deploy"
          msgX = "\n>>>>>>>>>>>>>>>>>>>>>>>>>> Changes are only allowed for USER [deploy].\n"
        }

        violation[{"msg": msgX}] {
          uname == "deploy"
          not weekdayOK
          msgX = "\n%%%%%%%%%%%%%%%%%%%%%%%%%% Changes are only allowed on [Monday] or [Wednesday].\n"
        }

        violation[{"msg": msgX}] {
          uname == "deploy"
          weekdayOK
          not timeOK
          msgX = "\n************************** Changes are only allowed in [23:30-06:00].\n"
        }
# kubectl apply -f constrainttemplateV1Beta1.yaml
# kubectl get ConstraintTemplate
# kubectl api-resources |grep gatekeeper
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
updatelimits                                   constraints.gatekeeper.sh/v1beta1      false        UpdateLimits

注意項

  1. spec.crd.spec.names.kind 小寫後必須和 metadata.name 一致
  2. violation返回值是一個結構體切片:[{"msg": STRING}]
violation[{"msg": message}] {
	message := ""
}

6 Constraint 約束條件

知識介紹(可以略過)

Constraint負責通知Gatekeeper,admin想要一個什麼樣的template
Constraint使用 K8sRequiredLabels 來約束上文中的模版,以便於確保 gatekeeper 標籤被定義到所有namespace上
可認為是對某一個 ConstraintTemplate 的例項化,其中對 ConstraintTemplate 的未指定引數進行了具體的配置。

注意spec.match欄位,它定義了 一個指定的約束 將要被應用到的 目標的範圍
Note the match field, which defines the scope of objects to which a given constraint will be applied.

支援的匹配專案:

  • kinds:接受一個物件列表,包含 apiGroups和 kinds,用來指定 約束將應用到的 物件組/種類的欄位。如果指定了多個物件,只要滿足其中一個就會被匹配
    only one match is needed for the resource to be in scope.
  • 目標範圍:允許 * ,Cluster 或者 Namespaced

此外還有:

  • namespaces:指定ns列表
  • excludedNamespaces:指定ns排除列表
  • labelSelector
  • namespaceSelector

注意:
如果定義了多個matcher,則匹配資源必須滿足所有頂級matcher
空matcher匹配所有
namespaces, excludedNamespaces, 和 namespaceSelector 將會匹配上沒有ns所屬的資源,為了避免這種情況,注意指定Namespace範圍。

約束參考

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: UpdateLimits
metadata:
  name: updatelimit-for-test
spec:
  match:
    namespaces: ["test"]
    kinds:
    - apiGroups: ["apps"]
      kinds:
      - "Deployment"
      - "DaemonSet"
      - "StatefulSet"

部署

kubectl apply -f ConstraintV1Beta1.yaml
kubectl get constraint

測試

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  name: nginx
  namespace: depl
spec:
  replicas: 0
  selector:
    matchLabels:
      io/os: linux
  template:
    metadata:
      labels:
        io/os: linux
    spec:
      containers:
      - image: nginx:latest
        name: nginx

shell指令碼:

#!/bin/sh
deployFile=nginx-deployment.yaml
echo
echo
echo
kubectl config use-context deploy
kubectl apply -f ${deployFile} -n depl &&  kubectl delete -f ${deployFile} -n depl
echo -e "\n\n\n"
kubectl config use-context kubernetes-admin@kubernetes

實驗結果:

root@master:~/OPAtest/MyTest# ./test.sh

Switched to context "deploy".
Error from server ([updatelimit-for-depl]
%%%%%%%%%%%%%%%%%%%%%%%%%% Changes are only allowed on [Monday] or [Wednesday].
): error when creating "nginx-deployment.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [updatelimit-for-depl]
%%%%%%%%%%%%%%%%%%%%%%%%%% Changes are only allowed on [Monday] or [Wednesday].


參考資料:

  1. 使用者與角色繫結:https://zhuanlan.zhihu.com/p/43237959
  2. Input物件:https://www.openpolicyagent.org/docs/latest/kubernetes-primer/#input-document
  3. 時間函式:https://www.openpolicyagent.org/docs/latest/policy-reference/#time
  4. rego實驗室:https://play.openpolicyagent.org/

相關文章