Kubernetes Validating Admission Policy : Introduction

Kubernetes Validating Admission Policy : Introduction

Depuis longtemps, au sein de l’écosystème Kubernetes, OPA (Open Policy Agent) et Kyverno sont instinctivement choisis pour mettre en place un admission controller. Plusieurs facteurs expliquent ce choix, notamment les lacunes de l’API Kubernetes à cet égard ou la flexibilité d’utiliser OPA dans d’autres scénarios comme l’infrastructure as code. Cependant, à l’heure actuelle, Kubernetes propose-t-il nativement des solutions matures et pertinentes sur cette thématique?

Le concept a été lancé avec la version 1.26 de Kubernetes et a atteint le stade beta dans la version 1.28.

L’un des points forts de cette fonctionnalité est son intégration directe à l’API de Kubernetes, éliminant le besoin de recourir à des tiers supplémentaires. Cela diminue ainsi l’effort requis pour la maintenance.

Validating Admission components

La logique de fond reste la même , si on prends l’exemple d’OPA par exemple :

Nous avons ces deux composants :

  • ConstraintTemplate qui défini la logique de la policy et qui contient le controle en language Rego à éffectuer par exemple : “maximum de replicas d’un déploiement ne peux pas éxéder 5”

  • Template utilise cette policy pour l’appliquer à tels ou tels composant POD , filtre la cible avec des parameters ….

Si on prends l’exemple de cette même policy mais pour la version native de Kubernetes , les composants principaux sont:

  • ValidatingAdmissionPolicy qui défini la policy de controle mais cette fois ci en language CEL

  • ValidatingAdmissionPolicyBinding est le même principe qu’un template il filtre le scope et parametre selon le contexte.

Validation admission controller in action

Afin de pouvoir tester cette feature, il est important d’avoir un environnement qui le permet. Minikube est vraiment intérressant pour tester de nouvelles Features comme celle-ci.

minikube start  --feature-gates='ValidatingAdmissionPolicy=true' \  
--kubernetes-version=1.28.0

Nous aimerions restreindre la possibilité de créer des services de type “NodePort” mais plutôt de centraliser l’exposition des applications avec un ingress ou un Kubernetes API gateway.:

Prenons maintenant un exemple avec OPA pour ce faire :

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sblocknodeport
  annotations:
    metadata.gatekeeper.sh/title: "Block NodePort"
    metadata.gatekeeper.sh/version: 1.0.0
    description: >-
      Disallows all Services with type NodePort.

      https://kubernetes.io/docs/concepts/services-networking/service/#nodeport
spec:
  crd:
    spec:
      names:
        kind: K8sBlockNodePort
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sblocknodeport

        violation[{"msg": msg}] {
          input.review.kind.kind == "Service"
          input.review.object.spec.type == "NodePort"
          msg := "User is not allowed to create service of type NodePort"
        }
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sBlockNodePort
metadata:
  name: block-node-port
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Service"]

Maintenant voyons comment faire cette équivalent via l’admission controller natif de kubernetes :

Validatingadmissionpolicy:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
  name: deny-nodeport
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   [""]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["services"]
  validations:
    - expression: object.spec.type != "NodePort"

Validatingadmissionpolicybinding:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "vincent-binding-test.example.com"
spec:
  policyName: "deny-nodeport"
  validationActions: [Deny]
  matchResources:
    namespaceSelector:
      matchLabels:
        env: prod

Le Kind ValidatingAdmissionPolicyBinding va dans notre cas appliquer cette policy sur l’ensemble des namespaces ayant un label env=prod

Maintenant faisons un petit test en déployant un simple service NodePort dans un namespace ayant un label env=prod

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: toto
  name: toto
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: toto
  type: NodePort
kubectl apply -f service.yaml -n titi 

The services "toto" is invalid: 
: ValidatingAdmissionPolicy 'deny-nodeport' with binding 
'deny-service-nodeport' denied request: failed expression:
 object.spec.type != "NodePort"

Nous pouvons observer que la Requête à été bloquée par Kubernetes.

Conclusion

Cette feature n’est pas encore ready for production mais va dans le bon sens. si on compare à Kyverno par exemple , Kyvernoo offre plus de flexibilité ou d’automatisation , par exemple en fournissant la capacité a provisionner de façon automatique des kinds dans certains namespace.

Si on parle d’OPA , lui est utilisable dans bien d’autres contextes que Kubernetes , Policy as code , authentication on demand et beaucoup d’autres contextes.