Validating Admission Policy を触ってみる
k8s v1.30 以降で stable になった Validating Admission Policy | Kubernetes を触ってみる。
kind で試す。
$ kind create cluster
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.32.0) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
$ k cluster-info
Kubernetes control plane is running at https://127.0.0.1:56902
CoreDNS is running at https://127.0.0.1:56902/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
$ k version
Client Version: v1.32.0
Kustomize Version: v5.5.0
Server Version: v1.32.0
とりあえずは公式ページのサンプルページ通りに進めてみる。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com"
spec:
failurePolicy: Fail # 条件に当てはまらない場合 Admission を失敗させる
matchConstraints:
resourceRules:
# deployment の作成・更新時において replicas が 5 以下
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas <= 5"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "demo-binding-test.example.com"
spec:
policyName: "demo-policy.example.com" # 上で定義した ValidatingAdmissionPolicy
validationActions: [Deny] # Validation に失敗した場合にリクエストを拒否する
matchResources:
namespaceSelector:
matchLabels:
environment: test
$ k apply -f sample.yaml
validatingadmissionpolicy.admissionregistration.k8s.io/demo-policy.example.com created
validatingadmissionpolicybinding.admissionregistration.k8s.io/demo-binding-test.example.com created
拒否されるような deployment を apply してみる。
apiVersion: v1
kind: Namespace
metadata:
name: test
labels:
environment: test # ValidatingAdmissionPolicyBinding で match されるように
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-deployment
namespace: test
spec:
replicas: 6 # 5以下の場合は許可される
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
$ k apply -f deployment.yaml
namespace/test created
The deployments "sample-deployment" is invalid: : ValidatingAdmissionPolicy 'demo-policy.example.com' with binding 'demo-binding-test.example.com' denied request: failed expression: object.spec.replicas <= 5
簡単な validation であればこれだけでできることはわかった。
ValidatingAdmissionPolicyBinding の Validation actions を理解したい。
以下の値が指定できるらしい。
-
Deny
: Validation に失敗した場合、リクエストを失敗させる -
Warn
: Validation に失敗した場合、クライアントに警告を出す -
Audit
: Validation に失敗した場合、監査イベントに記録される
先ほどの ValidatingAdmissionPolicyBinding を編集して試してみる。(先ほどは validationActions: [Deny]
)
Warn にしてみる。
$ k apply -f deployment.yaml
namespace/test unchanged
Warning: Validation failed for ValidatingAdmissionPolicy 'demo-policy.example.com' with binding 'demo-binding-test.example.com': failed expression: object.spec.replicas <= 5
deployment.apps/sample-deployment created
Deployment の作成自体は成功し、Warning:
は黄色く太文字で表示された。
exit code も 0。
$ echo $?
0
かち合うから当たり前だけども [Deny, Warn] の両方を設定することはできない。
Deny and Warn may not be used together since this combination needlessly duplicates the validation failure both in the API response body and the HTTP warning headers.
Audit は kind のクラスタの定義を変更しないと確認できなかったりするから動作確認は飛ばす。
次は Parameter resources を見てみる。
ポリシーの一部(可変にしたいところとか) を ValidatingAdmissionPolicy と分離して定義できる。
ドキュメント通りだと、ReplicaLimit
という Resource を定義しているが、CRD 自体の定義がなく動かして試すのが面倒なので、ConfigMap で代用できないか試してみる。
Namespaced なリソースは全て default におくようにして試した。
apiVersion: v1
kind: ConfigMap
metadata:
name: replica-limit
namespace: default
data:
maxReplicas: "5" # 文字列である必要がある
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com"
spec:
failurePolicy: Fail
paramKind:
apiVersion: v1
kind: ConfigMap
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas <= int(params.data.maxReplicas)" # 閾値を Parameter resource から引いてくるようになっている
reason: Invalid
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "demo-binding-test.example.com"
spec:
policyName: "demo-policy.example.com"
validationActions: [Deny]
paramRef:
name: "replica-limit"
namespace: "default"
parameterNotFoundAction: Deny # リソースが見つからない場合の動作を明示
$ k apply -f sample.yaml
configmap/replica-limit created
validatingadmissionpolicy.admissionregistration.k8s.io/demo-policy.example.com created
validatingadmissionpolicybinding.admissionregistration.k8s.io/demo-binding-test.example.com created
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-deployment
namespace: default
spec:
replicas: 6 # 5以下の場合は許可される
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
$ The deployments "sample-deployment" is invalid: : ValidatingAdmissionPolicy 'demo-policy.example.com' with binding 'demo-binding-test.example.com' denied request: failed expression: object.spec.replicas <= int(params.data.maxReplicas)
できた。
ConfigMap を Parameter resource として扱えることがわかったけど、すでに存在するリソースに沿ってバリデーションしたい場合結構便利かもしれない。
例えば、すでに存在する Deployment と同じイメージでしか新規の DaemonSet を作成できないというサンプルを作ってみる。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: daemonset
spec:
failurePolicy: Fail
paramKind:
apiVersion: apps/v1
kind: Deployment
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["daemonsets"]
validations:
- expression: "object.spec.template.spec.containers.all(\n container, \n container.image == params.spec.template.spec.containers[0].image\n)\n"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: daemonset
spec:
policyName: daemonset
validationActions: [Deny]
paramRef:
name: sample-deployment
namespace: default
parameterNotFoundAction: Deny # リソースが見つからない場合の動作を明示
$ k apply -f daemonset-policy.yaml
validatingadmissionpolicy.admissionregistration.k8s.io/daemonset created
validatingadmissionpolicybinding.admissionregistration.k8s.io/daemonset created
存在する Deployment(sample-deployment) と異なる Image を利用する Daemonset を作成する
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: sample-daemonset
namespace: default
spec:
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- name: nginx
image: nginx:1.22 # 存在するのは 1.21
$ k apply -f daemonset.yaml
The daemonsets "sample-daemonset" is invalid: : ValidatingAdmissionPolicy 'daemonset' with binding 'daemonset' denied request: failed expression: object.spec.template.spec.containers.all(
container,
container.image == params.spec.template.spec.containers[0].image
)
狙い通り失敗した。
一度は存在する Image で daemonset を作成し、その後書き換えるのはうまくいくはず。
$ cat daemonset.yaml| tail -n 1
image: nginx:1.21
$ k apply -f daemonset.yaml
daemonset.apps/sample-daemonset created
# 存在しないイメージに書き換え
$ cat daemonset.yaml| tail -n 1
image: nginx:1.22
$ k apply -f daemonset.yaml
daemonset.apps/sample-daemonset configured
OK.