Gatekeeperをインストールして、ポリシーの制御をしたときの備忘録。
TOC
Gatekeeperとは?
GatekeeperはOPAのサブプロジェクトで、柔軟なadmission webhookを使ってポリシーやガバナンスをKubernetesに適用することができる。
参考 Gatekeeper Githubこれにより、クラスタの利用者からのリソース作成リクエストなどに対して許可、拒否したり、ベストプラクティスを適用することができる。主なユースケースは以下の通り。
- Constraints
- APIサーバへのリクエストを許可したり拒否するポリシーを提供する
- Audit
- 既に作成されたリソースに対して、ポリシーに違反していないか定期的に確認する
- Dry-run
- 実際にポリシーを使って拒否はしないが、違反したポリシーが確認できる
- Handling uniqueness
- Ingressで指定されたHostnameがクラスタ内でユニークかどうかの確認などができる
- このユースケースには新規のリクエストと既存のリソースに対する情報が必要となる
ポリシーに関して、何らかのアイディアをすでに持っている場合には、どうやってGatekeeperを使って実装するか考えていけばいいが、そもそもGatekeeperで何ができるのかわからない方のために、よく使われるポリシーをまとめたライブラリが存在する。今回はこれらを使って実際の動作を見ていく。
参考 Gatekeeper Library Githubインストール
本ページにある通り下記コマンドを実行することでインストールできる。
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.4/deploy/gatekeeper.yaml
インストールしたときのログは以下の通り。
# kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.4/deploy/gatekeeper.yaml
namespace/gatekeeper-system created
Warning: apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition
customresourcedefinition.apiextensions.k8s.io/configs.config.gatekeeper.sh created
customresourcedefinition.apiextensions.k8s.io/constraintpodstatuses.status.gatekeeper.sh created
customresourcedefinition.apiextensions.k8s.io/constrainttemplatepodstatuses.status.gatekeeper.sh created
customresourcedefinition.apiextensions.k8s.io/constrainttemplates.templates.gatekeeper.sh created
serviceaccount/gatekeeper-admin created
podsecuritypolicy.policy/gatekeeper-admin created
role.rbac.authorization.k8s.io/gatekeeper-manager-role created
clusterrole.rbac.authorization.k8s.io/gatekeeper-manager-role created
rolebinding.rbac.authorization.k8s.io/gatekeeper-manager-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/gatekeeper-manager-rolebinding created
secret/gatekeeper-webhook-server-cert created
service/gatekeeper-webhook-service created
deployment.apps/gatekeeper-audit created
deployment.apps/gatekeeper-controller-manager created
poddisruptionbudget.policy/gatekeeper-controller-manager created
Warning: admissionregistration.k8s.io/v1beta1 ValidatingWebhookConfiguration is deprecated in v1.16+, unavailable in v1.22+; use admissionregistration.k8s.io/v1 ValidatingWebhookConfiguration
validatingwebhookconfiguration.admissionregistration.k8s.io/gatekeeper-validating-webhook-configuration created
下記のリソースが作成されている。
- Namespace
- namespace/gatekeeper-system
- CRD
- customresourcedefinition.apiextensions.k8s.io/configs.config.gatekeeper.sh
- customresourcedefinition.apiextensions.k8s.io/constraintpodstatuses.status.gatekeeper.sh
- customresourcedefinition.apiextensions.k8s.io/constrainttemplatepodstatuses.status.gatekeeper.sh
- customresourcedefinition.apiextensions.k8s.io/constrainttemplates.templates.gatekeeper.sh
- SA
- serviceaccount/gatekeeper-admin
- PSP
- podsecuritypolicy.policy/gatekeeper-admin
- Role
- role.rbac.authorization.k8s.io/gatekeeper-manager-role
- ClusterRole
- clusterrole.rbac.authorization.k8s.io/gatekeeper-manager-role
- RoleBinding
- rolebinding.rbac.authorization.k8s.io/gatekeeper-manager-rolebinding
- ClusterRoleBinding
- clusterrolebinding.rbac.authorization.k8s.io/gatekeeper-manager-rolebinding
- Secret
- secret/gatekeeper-webhook-server-cert
- Service
- service/gatekeeper-webhook-service
- Deployment
- deployment.apps/gatekeeper-audit
- deployment.apps/gatekeeper-controller-manager
- PodDiruptionBudget
- poddisruptionbudget.policy/gatekeeper-controller-manager
- ValidatingWebhookConfiguration
- validatingwebhookconfiguration.admissionregistration.k8s.io/gatekeeper-validating-webhook-configuration
動作確認
シナリオ
- 全てのNamespaceがコンタクト先となるラベルをもつこと
- 全てのPodがリソースのリミットを指定していること
- 全てのImageが許可されたレポジトリからのみであること
- ServiceのNodePortは許可しない
全てのNamespaceがコンタクト先となるラベルをもつこと
このデモでは、Namespaceが必ずowner
というラベルを持つようにする。
まず、ConstraintTemplateを作ります。
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/requiredlabels/template.yaml
中身をみてみると、
.spec.crd
にてCRDのスキームを作成.spec.targets.rego
内でRegoを使ってポリシーを記述
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
annotations:
description: Requires all resources to contain a specified label with a value
matching a provided regular expression.
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
# Schema for the `parameters` field
openAPIV3Schema:
properties:
message:
type: string
labels:
type: array
items:
type: object
properties:
key:
type: string
allowedRegex:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
get_message(parameters, _default) = msg {
not parameters.message
msg := _default
}
get_message(parameters, _default) = msg {
msg := parameters.message
}
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_].key}
missing := required - provided
count(missing) > 0
def_msg := sprintf("you must provide labels: %v", [missing])
msg := get_message(input.parameters, def_msg)
}
violation[{"msg": msg}] {
value := input.review.object.metadata.labels[key]
expected := input.parameters.labels[_]
expected.key == key
# do not match if allowedRegex is not defined, or is an empty string
expected.allowedRegex != ""
not re_match(expected.allowedRegex, value)
def_msg := sprintf("Label <%v: %v> does not satisfy allowed regex: %v", [key, value, expected.allowedRegex])
msg := get_message(input.parameters, def_msg)
}
ConstraintTemplateを作ると下記のConstraintのCRDが作成されます
# kubectl get crd k8srequiredlabels.constraints.gatekeeper.sh
NAME CREATED AT
k8srequiredlabels.constraints.gatekeeper.sh 2021-06-25T02:38:35Z
このCRDを使って、Namespaceに特定のラベルが必要というポリシーが記述できます。
今回はサンプルを編集して以下のようなConstraintを作成します。
# constraint.yaml
# Based on https://github.com/open-policy-agent/gatekeeper-library/blob/master/library/general/requiredlabels/samples/all-must-have-owner/constraint.yamlapiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: all-must-have-owner
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
message: "All namespaces must have an `owner` label that points to your company username"
labels:
- key: owner
allowedRegex: "^[a-zA-Z]+$"
適用します。
kubectl apply -f ./constraint.yaml
これにより、Constraintリソースが以下のように作成されました。
# kubectl get k8srequiredlabels
NAME AGE
all-must-have-owner 35s
この状態でNamespaceを作ろうとするとエラーがでます。
# kubectl create ns ng-ns
Error from server ([all-must-have-owner] All namespaces must have an `owner` label that points to your company username): admission webhook "validation.gatekeeper.sh" denied the request: [all-must-have-owner] All namespaces must have an `owner` label that points to your company username
そのため、ownerラベルがついた下記のようなマニフェストを適用する必要があります。
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: null
name: ok-ns
labels:
owner: john
spec: {}
status: {}
また、Audit機能により、既存のリソースで違反するものも確認できます。
# kubectl describe k8srequiredlabels all-must-have-owner
Name: all-must-have-owner
Namespace:
Labels: <none>
Annotations: <none>
API Version: constraints.gatekeeper.sh/v1beta1
Kind: K8sRequiredLabels
<snip>
Status:
Audit Timestamp: 2021-06-25T04:59:17Z
<snip>
Total Violations: 1
Violations:
Enforcement Action: deny
Kind: Namespace
Message: All namespaces must have an `owner` label that points to your company username
Name: test-ns
Events: <none>
全てのPodがリソースのリミットを指定していること
Podにリソースリミットが指定されていない場合には拒否するシナリオです。
まず、ConstraintTemplateを読み込みます。
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/containerlimits/template.yaml
続いて、サンプルのConstraintを読み込みます。
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/containerlimits/samples/container-must-have-limits/constraint.yaml
これにより下記のk8scontainerlimitリソースが作成されているはずです。
# kubectl get k8scontainerlimits container-must-have-limits
NAME AGE
container-must-have-limits 113s
この状態で、podを作成しようとすると、下記の通りエラーがでるようになります。
# kubectl run nginx --image=nginx
Error from server ([container-must-have-limits] container <nginx> has no resource limits): admission webhook "validation.gatekeeper.sh" denied the request: [container-must-have-limits] container <nginx> has no resource limits
全てのImageが許可されたレポジトリからのみであること
許可されたレポジトリからのみに制限するポリシーを作成します。
同様に、ライブラリレポにあるマニフェストを適用します。
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/allowedrepos/template.yaml
今回はdefaultのNamespaceではdocker.ioのレポからのみを許可します。
# constraint-repo-docker.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
name: repo-is-docker
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- "default"
parameters:
repos:
- "docker.io/"
こちらのConstraintを適用します。
kubectl apply -f ./constraint-repo-docker.yaml
その後、docker.ioがImageのパスに入っていないとリクエストが拒否されます。
# kubectl run nginx --image=nginx
Error from server ([repo-is-docker] container <nginx> has an invalid image repo <nginx>, allowed repos are ["docker.io/"]): admission webhook "validation.gatekeeper.sh" denied the request: [repo-is-docker] container <nginx> has an invalid image repo <nginx>, allowed repos are ["docker.io/"]
デフォルトでdocker.ioからイメージをダウンロードしようとするので、下記のケースと結果はかわりません。
# kubectl run nginx --image=docker.io/nginx
pod/nginx created
ただし、明示的にdocker.io
を使用するようにしたため、今回は成功しています。
ServiceのNodePortは許可しない
続いて、NodePortが作成できないようします。
今までと同様にライブラリからConstraintTemplateを適用します。
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/block-nodeport-services/template.yaml
続いて、Constraintを適用します。本件に関しては、カスタマイズするポイントもないので、そのまま使用します。
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/block-nodeport-services/samples/block-node-port/constraint.yaml
この状態でpodをNodeportでexposeしようとすると下記のようなエラーになります。
# kubectl expose pod nginx --port=80 --type=NodePort
Error from server ([block-node-port] User is not allowed to create service of type NodePort): admission webhook "validation.gatekeeper.sh" denied the request: [block-node-port] User is not allowed to create service of type NodePort