kakakakakku blog

Weekly Tech Blog : Keep on Learning 👍

Kubernetes と OPA を組み合わせて Namespace と Pod の「ラベル強制」を試した

今回は Kubernetes と OPA (Open Policy Agent) を組み合わせて「ラベル強制」を試す.

OPA Gatekeeper をインストールする

まず,Kubernetes クラスターに OPA Gatekeeper をインストールする.今回は OPA のドキュメントに載っている kubectl apply コマンドを使う.他にも Helm を使うこともできる.

open-policy-agent.github.io

kubectl apply コマンドでインストールをすると,多くの Kubernetes オブジェクトが適用される.重要なのは CRD (Custom Resource Definition) で,以下の4種類の CRD が適用されている.

  • configs.config.gatekeeper.shkind: Config
  • constraintpodstatuses.status.gatekeeper.shkind: ConstraintPodStatus
  • constrainttemplatepodstatuses.status.gatekeeper.shkind: ConstraintTemplatePodStatus
  • constrainttemplates.templates.gatekeeper.shkind: ConstraintTemplate
$ kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.5/deploy/gatekeeper.yaml
namespace/gatekeeper-system created
resourcequota/gatekeeper-critical-pods created
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
validatingwebhookconfiguration.admissionregistration.k8s.io/gatekeeper-validating-webhook-configuration created

$ kubectl get crds
NAME                                                 CREATED AT
configs.config.gatekeeper.sh                         2021-07-05T07:00:00Z
constraintpodstatuses.status.gatekeeper.sh           2021-07-05T07:00:00Z
constrainttemplatepodstatuses.status.gatekeeper.sh   2021-07-05T07:00:00Z
constrainttemplates.templates.gatekeeper.sh          2021-07-05T07:00:00Z

CRD k8srequiredlabels.constraints.gatekeeper.sh を追加する

さっそく「ラベル強制」のために必要な設定を適用する.まず,ConstraintTemplate から「ラベル強制」をする CRD として K8sRequiredLabels を作る.強制するためには Rego を使って実装をする必要があるけど,詳細は割愛して,今回は OPA の GitHub リポジトリに公開されているサンプルとして,k8srequiredlabels_template.yaml をそのまま使う.

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        # Schema for the `parameters` field
        openAPIV3Schema:
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels
        violation[{"msg": msg, "details": {"missing_labels": missing}}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("you must provide labels: %v", [missing])
        }

kubectl apply コマンドで k8srequiredlabels_template.yaml を適用すると,新しく CRD として k8srequiredlabels.constraints.gatekeeper.sh が追加された.

$ kubectl apply -f k8srequiredlabels_template.yaml

$ kubectl get crds
NAME                                                 CREATED AT
configs.config.gatekeeper.sh                         2021-07-05T00:00:00Z
constraintpodstatuses.status.gatekeeper.sh           2021-07-05T00:00:00Z
constrainttemplatepodstatuses.status.gatekeeper.sh   2021-07-05T00:00:00Z
constrainttemplates.templates.gatekeeper.sh          2021-07-05T00:00:00Z
k8srequiredlabels.constraints.gatekeeper.sh          2021-07-05T00:01:00Z

Namespace で「ラベル強制」を試す

まず,Namespace「ラベル強制」を試す.以下のように K8sRequiredLabels でマニフェストを作る.ポイントは Namespace に対して app ラベルを強制するため,もし app ラベルが付いていない場合はリソースの作成に失敗する.

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: namespace-app-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["app"]

実際に namespace-app-label.yaml を適用する.

$ kubectl apply -f namespace-app-label.yaml

$ kubectl get k8srequiredlabels.constraints.gatekeeper.sh 
NAME                  AGE
namespace-app-label   30s

さっそく以下の3種類のマニフェストを適用する.

  • 🆖 ns1.yaml ... app ラベルなし
  • 🆖 ns2.yaml ... app ラベルなし(env ラベルあり)
  • 🆗 ns3.yaml ... app ラベルあり(env ラベルあり)

📁 ns1.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: kakakakakku-ns

📁 ns2.yaml

apiVersion: v1
kind: Namespace
metadata:
  labels:
    env: dev
  name: kakakakakku-ns

📁 ns3.yaml

apiVersion: v1
kind: Namespace
metadata:
  labels:
    app: my-app
    env: dev
  name: kakakakakku-ns

結果として ns1.yamlns2.yaml「ラベル強制」の結果エラーになる.you must provide labels: {"app"} というエラーも出ている.

$ kubectl apply -f ns1.yaml
Error from server ([namespace-app-label] you must provide labels: {"app"}): error when creating "ns1.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [namespace-app-label] you must provide labels: {"app"}

$ kubectl apply -f ns2.yaml
Error from server ([namespace-app-label] you must provide labels: {"app"}): error when creating "ns2.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [namespace-app-label] you must provide labels: {"app"}

$ kubectl apply -f ns3.yaml
namespace/kakakakakku-ns created

Pod で「ラベル強制」を試す

次に,Pod「ラベル強制」を試す.同じく K8sRequiredLabels でマニフェストを作る.ポイントは Pod に対して app ラベルを強制するため,もし app ラベルが付いていない場合はリソースの作成に失敗する.

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: pod-app-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
  parameters:
    labels: ["app"]

実際に pod-app-label.yaml を適用する.

$ kubectl apply -f pod-app-label.yaml

$ kubectl get k8srequiredlabels.constraints.gatekeeper.sh
NAME                  AGE
namespace-app-label   15m
pod-app-label         30s

さっそく以下の3種類のマニフェストを適用する.

  • 🆖 pod1.yaml ... app ラベルなし
  • 🆖 pod2.yaml ... app ラベルなし(env ラベルあり)
  • 🆗 pod3.yaml ... app ラベルあり(env ラベルあり)

📁 pod1.yaml

apiVersion: v1
kind: Pod
metadata:
  name: kakakakakku-pod
spec:
  containers:
    - name: nginx
      image: nginx:1.21

📁 pod2.yaml

apiVersion: v1
kind: Pod
metadata:
  name: kakakakakku-pod
  labels:
    env: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.21

📁 pod3.yaml

apiVersion: v1
kind: Pod
metadata:
  name: kakakakakku-pod
  labels:
    app: my-app
    env: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.21

結果として pod1.yamlpod2.yaml「ラベル強制」の結果エラーになる.you must provide labels: {"app"} というエラーも出ている.

$ kubectl apply -f pod1.yaml
Error from server ([pod-app-label] you must provide labels: {"app"}): error when creating "pod1.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [pod-app-label] you must provide labels: {"app"}

$ kubectl apply -f pod2.yaml
Error from server ([pod-app-label] you must provide labels: {"app"}): error when creating "pod2.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [pod-app-label] you must provide labels: {"app"}

$ kubectl apply -f pod3.yaml
pod/kakakakakku-pod created

まとめ

今回は Kubernetes と OPA (Open Policy Agent) を組み合わせて「ラベル強制」を試した.NamespacePod に対して,もし app ラベルが付いていない場合はリソースの作成に失敗するようにできた.引き続き OPA を試していくぞ!