Gatekeeperをインストールしてみる

Gatekeeperをインストールして、ポリシーの制御をしたときの備忘録。

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

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA