Thực hành viết OPA Gatekeeper Kubernetes Policy

Thực hành viết OPA Gatekeeper Kubernetes PolicyCuongquach.com | Bài viết này là sẽ hướng dẫn bạn cách thực hành viết OPA Gatekeeper Policy cho Kubernetes nhằm quản lý các tiêu chuẩn an toàn khi khởi tạo/cập nhật Kubernetes Resource.

Giả sử mình chuẩn bị một OPA Gatekeeper Policy với nội dung như sau:

  • Tên: Deployment001MinimumReplicas

  • Đối tượng: Deployment (apiGroups: apps)

  • Miêu tả: với các deployment có label [“env”] là production thì phải có replicas setting tối thiểu là 2. Còn các môi trường khác thì tối thiểu là 1.

Thực hành viết OPA Gatekeeper Kubernetes Policy

Thực hành practice 1

+ Lấy mẫu input JSON Object

Điều quan trọng đầu tiên là chúng ta làm cách nào để có thể lấy được dữ liệu JSON Object thô (raw) mà API Kubernetes gửi đến Adminssion Controller . Từ đó chúng ta mới có data để có thể xử lý dữ liệu bằng code Rego được. Đây là một thủ thuật hay sử dụng.

Mình tạo một Constraint Template sẽ từ chối tất cả request liên quan đến namespace opa-testing và in ra nội dung của JSON Object ra output message.

Bước 1: tạo namespace opa-data-sample

# mkdir practice-1
# mkdir -p practice-1/sample/
# cd practice-1/sample/

# vi ns-opa-data-sample.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: "opa-data-sample"
  labels:
    name: "opa-data-sample"

# kubectl apply -f ns-opa-data-sample.yaml
namespace/opa-data-sample created

Bước 2: tạo một Constraint Template với mục đích in ra nội dung của Object Review nhận được từ Admission Controller.

# cd practice-1/sample/
# vi constraint-template-k8sdenyall.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8sdenyall
  namespace: opa-data-sample
spec:
  crd:
    spec:
      names:
        kind: K8sDenyAll
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sdenyall

        violation[{"msg": msg}] {
          msg := sprintf("REVIEW OBJECT: %v", [input])
        }
        
# kubectl apply -f constraint-template-k8sdenyall.yaml
constrainttemplate.templates.gatekeeper.sh/k8sdenyall created

Bước 3: tạo một Constraint opa-data-sample-deployment sử dụng template k8sdenyall , áp dụng cho mỗi namespace opa-data-sample.

# cd practice-1/sample/
# vi constraint-k8sdenyall.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDenyAll
metadata:
  name: opa-data-sample-deployment
spec:
  match:
    namespaces: ["opa-data-sample"]
    kinds:
      - apiGroups: ["apps", "extensions"]
        kinds: ["Deployment"]
        
# kubectl apply -f constraint-k8sdenyall.yaml

Bước 4: tạo một file manifest Kubernetes Deployment đơn giản để apply khởi tạo resource trên namespace opa-data-sample.

# cd practice-1/sample/
# vi deployment-sample.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: opa-data-sample
  name: helloweb
  labels:
    app: hello
    env: production
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello
      tier: web
  template:
    metadata:
      labels:
        app: hello
        tier: web
    spec:
      containers:
      - name: hello-app
        image: gcr.io/google-samples/hello-app:1.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: 200m

Bước 5: apply file manifest Deployment để xem Gatekeeper từ chối request khởi tạo và in ra thông tin về

# cd practice-1/sample/
# kubectl apply -f deployment-sample.yaml
error when creating "deployment-sample.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [opa-data-sample-deployment] REVIEW OBJECT: {"parameters": {}, "review": {"_unstable": {"namespace": {"apiVersion": "v1", "kind": "Namespace", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{},\"labels\":{\"name\":\"opa-data-sample\"},\"name\":\"opa-data-sample\"}}\n"}, "creationTimestamp": "2021-06-26T14:55:43Z", "labels": {"name": "opa-data-sample"}, "managedFields": [{"apiVersion": "v1", "fieldsType": "FieldsV1", "fieldsV1": {"f:metadata": {"f:annotations": {".": {}, "f:kubectl.kubernetes.io/last-applied-configuration": {}}, "f:labels": {".": {}, "f:name": {}}}, "f:status": {"f:phase": {}}}, "manager": "kubectl", "operation": "Update", "time": "2021-06-26T14:55:43Z"}], "name": "opa-data-sample", "resourceVersion": "8895353", "selfLink": "/api/v1/namespaces/opa-data-sample", "uid": "a9f64db1-15de-4be9-9db5-7977a6aa12fc"}, "spec": {"finalizers": ["kubernetes"]}, "status": {"phase": "Active"}}}, "dryRun": false, "kind": {"group": "apps", "kind": "Deployment", "version": "v1"}, "name": "helloweb", "namespace": "opa-data-sample", "object": {"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"hello\",\"env\":\"production\"},\"name\":\"helloweb\",\"namespace\":\"opa-data-sample\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"hello\",\"tier\":\"web\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"hello\",\"tier\":\"web\"}},\"spec\":{\"containers\":[{\"image\":\"gcr.io/google-samples/hello-app:1.0\",\"name\":\"hello-app\",\"ports\":[{\"containerPort\":8080}],\"resources\":{\"requests\":{\"cpu\":\"200m\"}}}]}}}}\n"}, "creationTimestamp": "2021-06-26T15:11:33Z", "generation": 1, "labels": {"app": "hello", "env": "production"}, "managedFields": [{"apiVersion": "apps/v1", "fieldsType": "FieldsV1", "fieldsV1": {"f:metadata": {"f:annotations": {".": {}, "f:kubectl.kubernetes.io/last-applied-configuration": {}}, "f:labels": {".": {}, "f:app": {}, "f:env": {}}}, "f:spec": {"f:progressDeadlineSeconds": {}, "f:replicas": {}, "f:revisionHistoryLimit": {}, "f:selector": {"f:matchLabels": {".": {}, "f:app": {}, "f:tier": {}}}, "f:strategy": {"f:rollingUpdate": {".": {}, "f:maxSurge": {}, "f:maxUnavailable": {}}, "f:type": {}}, "f:template": {"f:metadata": {"f:labels": {".": {}, "f:app": {}, "f:tier": {}}}, "f:spec": {"f:containers": {"k:{\"name\":\"hello-app\"}": {".": {}, "f:image": {}, "f:imagePullPolicy": {}, "f:name": {}, "f:ports": {".": {}, "k:{\"containerPort\":8080,\"protocol\":\"TCP\"}": {".": {}, "f:containerPort": {}, "f:protocol": {}}}, "f:resources": {".": {}, "f:requests": {".": {}, "f:cpu": {}}}, "f:terminationMessagePath": {}, "f:terminationMessagePolicy": {}}}, "f:dnsPolicy": {}, "f:restartPolicy": {}, "f:schedulerName": {}, "f:securityContext": {}, "f:terminationGracePeriodSeconds": {}}}}}, "manager": "kubectl", "operation": "Update", "time": "2021-06-26T15:11:33Z"}], "name": "helloweb", "namespace": "opa-data-sample", "uid": "9cda3955-456f-4fb4-87f7-d7322512593f"}, "spec": {"progressDeadlineSeconds": 600, "replicas": 1, "revisionHistoryLimit": 10, "selector": {"matchLabels": {"app": "hello", "tier": "web"}}, "strategy": {"rollingUpdate": {"maxSurge": "25%", "maxUnavailable": "25%"}, "type": "RollingUpdate"}, "template": {"metadata": {"creationTimestamp": null, "labels": {"app": "hello", "tier": "web"}}, "spec": {"containers": [{"image": "gcr.io/google-samples/hello-app:1.0", "imagePullPolicy": "IfNotPresent", "name": "hello-app", "ports": [{"containerPort": 8080, "protocol": "TCP"}], "resources": {"requests": {"cpu": "200m"}}, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Always", "schedulerName": "default-scheduler", "securityContext": {}, "terminationGracePeriodSeconds": 30}}}, "status": {}}, "oldObject": null, "operation": "CREATE", "options": {"apiVersion": "meta.k8s.io/v1", "kind": "CreateOptions"}, "requestKind": {"group": "apps", "kind": "Deployment", "version": "v1"}, "requestResource": {"group": "apps", "resource": "deployments", "version": "v1"}, "resource": {"group": "apps", "resource": "deployments", "version": "v1"}, "uid": "3a9bbddd-6c48-495b-ad01-0401e354b23c", "userInfo": {"extra": {"accessKeyId": ["ASIA5MZIFYPAYJVND5HF"]}, "groups": ["system:masters", "system:authenticated"], "uid": "heptio-authenticator-aws:920817877953:AROA5MZIFYPA6VD7RK25U", "username": "admin"}}}

Bước 6: code OPA Gatekeeper ConstraintConstraint Template với các lưu ý sau:
– Sử dụng tool online: The Rego Playground để code cho tiện.

# mkdir -p practice-1/code/
# cd practice-1/code/
# vi deployment-001-minimum-replicas-prod.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: deployment001minimumreplicas
spec:
  crd:
    spec:
      names:
        kind: Deployment001MinimumReplicas
        listKind: deployment001minimumreplicas
        plural: deployment001minimumreplicas
        singular: deployment001minimumreplicas
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
          package deployment001minimumreplicas

          violation[{"msg": msg}] {
            # This rule applis to Kubernetes Deployment resource
            is_deployment

              env_min_replicas = {
              "production": 2,
              "staging": 1,
              "development": 1
              }

              # Note: when play on https://play.openpolicyagent.org/
              # Please remove '.review'
              env := input.review.object.metadata.labels.env
              replicas := input.review.object.spec.replicas
              env_min_replicas[env]; replicas < env_min_replicas[env]
              msg := sprintf("You deployment does not meet minimum replica setting for env [%v]", [env])
          }

          is_deployment {
              input.review.kind.kind    == "Deployment"
              input.review.kind.group   == "apps"
              input.review.kind.version == "v1"
          }
# cd practice-1/code/
# vi contrainst-deployment-001.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: Deployment001MinimumReplicas
metadata:
  name: deployment-001-minimum-replicas
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]

Apply Constraint và Constraint Template:

# kubectl apply -f deployment-001-minimum-replicas-prod.yaml
# kubectl apply -f contrainst-deployment-001.yaml

Apply deployment sample manifest với replicas 1.

# kubectl apply -f deployment-sample-failed.yaml
Error from server ([deployment-001-minimum-replicas] You deployment does not meet minimum replica setting for env [production]): error when creating "deployment-sample.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [deployment-001-minimum-replicas] You deployment does not meet minimum replica setting for env [production]

Sau đó change replicas sang 3, apply lại thử.

# kubectl apply -f deployment-sample-ok.yaml
Previous articleCấu hình uỷ quyền quản lý sub-domain sang AWS Route53 Hosted Zone khác
Next articleCấu hình gửi docker container log lên AWS Cloudwatch Logs
Bạn đang theo dõi website "https://cuongquach.com/" nơi lưu trữ những kiến thức tổng hợp và chia sẻ cá nhân về Quản Trị Hệ Thống Dịch Vụ & Mạng, được xây dựng lại dưới nền tảng kinh nghiệm của bản thân mình, Quách Chí Cường. Hy vọng bạn sẽ thích nơi này !