Thực hành viết OPA Gatekeeper Kubernetes Policy – Cuongquach.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 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 Constraint và Constraint 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