Gatekeeperとは
-
GatekeeperとはOpen Policy Agentのポリシーエンジンの一種であり、Kubernetesクラスタ内でのリソース作成時にポリシーを課すことができ、ポリシーを満たさないリソースの作成を防止してくれるツールです。例えば、「Deploymentにはappタグが必要である」というポリシーを課すことで、そのポリシーを満たさないDeploymentがクラスタに作成されることを防いでくれます。
-
これはAdmission WebhooksのValidationを行う際にポリシーを満たしているかのチェックが行われることで実現されています。そのため、
kubectl
やhelm install
、CRD(Custom Resource Definition)からの作成など、あらゆるデプロイ方法に適用されます。 -
他の代表的なポリシーエンジンとして、Conftestが知られています。こちらはコマンドラインからマニフェストファイルがポリシーを満たしているかをチェックするツールであり、主にCIで使用されています。ただし、ConftestだけだとCRDがクラスタ内部で作成するリソースなど、明示的にマニフェストに明記されていないリソースまでチェックすることができません。そのため、ConftestとGatekeeperを両方とも使用してリソースにポリシーを課す運用がベターだと思います。なお、どちらのツールでもRegoという言語を使用してポリシーを記述します。
インストール
- Helmでインストールする方法が簡単です。
$ helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
$ helm install gatekeeper/gatekeeper --generate-name
- これにより必要なリソースと、いくつかのCRDが定義されます。
ポリシーの作成
- 実際にポリシーを作ってみます。ここでは、Nodeが行う排出処理が成功するよう、pdb(PodDisruptionBudget)のmaxUnavailableに0より大きい値が設定されていることを課すポリシーを作成してみます。主に必要なリソースは以下の2つです。
- ConstraintTemplate
- constraintの雛形であり、Regoで記載したポリシーを併せて定義します。これを作成することで、
spec.crd.spec.names.kind
で定義したCRDが作成されます。 - targets.regoにポリシーが記載されています。詳細は省略しますが、ポイントは以下の通りです。
- ルールの名前は
violation
である必要があります。 -
input.review
に適用されるマニフェストの内容が設定されています。ここの値はポリシーを課すリソースによって変更する必要があります。設定の内容については後述。 -
:=
は代入を意味し、ここではmaxUnavailable
という変数にmaxUnavailable
の値を代入しています。 -
maxUnavailable
の数値を評価して結果をok
という変数に代入し、それを配列satisfied
の要素としています。 -
not all
が評価を行っている箇所になります。all
は全ての要素がtrueだとtrueを返す関数であり、そこにnotが付与されているため、条件を満たさない場合にtrueを返すことになります。 -
msg
はtrueのとき(必要なポリシーを満たしていないとき)に出力するメッセージであり、必須のパラメータです。
- ルールの名前は
- constraintの雛形であり、Regoで記載したポリシーを併せて定義します。これを作成することで、
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: pdbmaxunavailable
spec:
crd:
spec:
names:
kind: PdbMaxUnavailable
validation:
openAPIV3Schema:
properties:
maxUnavailable:
type: integer
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package pdbmaxunavailable
violation[{"msg": msg, "details": {}}] {
maxUnavailable := input.review.object.spec.maxUnavailable
satisfied := [ok | ok := maxUnavailable > 0]
not all(satisfied)
msg := "maxUnavailable must be bigger than 0."
}
- PdbMaxUnavailable
- 上記のConstraintTemplateで定義したConstraintです。
-
spec.match.kinds.kinds
で対象のリソースを、spec.match.kinds.apiGroups
でリソースのAPI Groupを指定します。
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: PdbMaxUnavailable
metadata:
name: maxunavailable-must-be-bigger-than-zero
spec:
match:
kinds:
- apiGroups: ["policy"]
kinds: ["PodDisruptionBudget"]
稼働確認
- 上記の
constraintTemplate.yaml
とpdbMaxUnavailable.yaml
をapplyし、pdbを作成します。 - その後、以下のようにポリシーを満たさないpdbの作成を試みます。
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: sample-app
spec:
maxUnavailable: 0
selector:
matchLabels:
app: sample-app
- applyするとConstraintTemplateで定義したメッセージが出力され、リソースの作成が失敗します。
Error from server ([denied by maxunavailable-must-be-bigger-than-zero] maxUnavailable must be bigger than 0.): error when creating "fail/pdb.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [denied by maxunavailable-must-be-larger-than-zero] maxUnavailable must be bigger than 0.
その他
デバッグ
- 上述の通り、
ConstraintTemplate
を作成する際にinput.review
内の要素を指定する必要がありますが、リソース毎に構成が異なるため、設定前に確認する必要があります。これはmsg
の出力にinput.review
を指定したConstraintTemplateを作成することで確認できます。特に条件を指定していないため、全てのpdbの作成時にメッセージが出力されます。
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8sdenyall
spec:
crd:
spec:
names:
kind: K8sDenyAll
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdenyall
violation[{"msg": msg}] {
msg := sprintf("REVIEW OBJECT: %v", [input.review])
}
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDenyAll
metadata:
name: deny-all-pdb
spec:
match:
kinds:
- apiGroups: ["policy"]
kinds: ["PodDisruptionBudget"]
- 適当なpdbを作成しようとすると、以下のように
input.review
以下の要素が出力され、この内容を基に評価対象のオブジェクトをRegoで記載しましょう。やや面倒な手順ですが、READMEにも記載されている方法ですので、現状はこの方法しかないようです。
"object": {
"apiVersion": "policy/v1beta1",
"kind": "PodDisruptionBudget",
"metadata": {
"name": "myapp",
"namespace": "default",
"uid": "da6b703c-83d6-4488-910c-c7492343eddc",
"generation": 1,
"spec": {
"selector": {
"matchLabels": {
"run": "myapp"
}
},
"maxUnavailable": 0
},
}
リソース作成が失敗した際のログ
- デフォルトの設定だと、Gatekeeperによってポリシーを満たさないリソース作成が阻止されてもログが出力されません。出力するためには
gatekeeper-controller-manager
というDeploymentで、コンテナのargsに--log-denies
を指定する必要があります。 - リソースの作成が阻止されると以下のようなログが確認できます。
{"level":"info","ts":1611475124.4214969,"logger":"webhook","msg":"denied admission","process":"admission","event_type":"violation","constraint_name":"maxunavailable-must-be-bigger-than-zero","constraint_group":"constraints.gatekeeper.sh","constraint_api_version":"v1beta1","constraint_kind":"PdbMaxUnavailable","constraint_action":"deny","resource_group":"policy","resource_api_version":"v1beta1","resource_kind":"PodDisruptionBudget","resource_namespace":"default","resource_name":"sample-app","request_username":xxxxx"}
まとめ
- Kubernetesクラスタ内でリソースにポリシーを課し、それを満たさない場合にはリソースの作成を阻止するGatekeeperについてpdbを例として記載しました。
- ConstraintTemplateにてRegoでポリシーを記載し、Constraintで対象のリソースを指定することで、クラスタ内のリソース作成に関するポリシーを適用することができます。
参考
- open-policy-agent/gatekeeper
- open-policy-agent/frameworks
- [Open Policy Agent | Policy Language](Policy Language)