Écrire un modèle de contrainte personnalisé

Cette page explique comment écrire un modèle de contrainte personnalisé et l'utiliser pour étendre Policy Controller si vous ne trouvez pas de modèle de contrainte prédéfini adaptée à vos besoins.

Les règles de Policy Controller sont décrites à l'aide du service OPA Contraint Framework et sont écrites dans Rego. Une règle peut évaluer n'importe quel champ d'un objet Kubernetes.

Écrire des règles à l'aide de Rego est une compétence spécialisée. Pour cette raison, une bibliothèque de modèles de contraintes courants est installée par défaut. Vous pouvez probablement invoquer ces modèles de contraintes lors de la création de contraintes. Si vous avez des besoins spécifiques, vous pouvez créer vos propres modèles de contraintes.

Les modèles de contraintes vous permettent de séparer la logique d'une règle de ses exigences spécifiques, à des fins de réutilisation et de délégation. Vous pouvez créer des contraintes à l'aide de modèles de contraintes développés par des tiers, tels que des projets Open Source, des éditeurs de logiciels ou des experts en réglementation.

Avant de commencer

Exemple de modèle de contrainte

Vous trouverez ci-dessous un exemple de modèle de contrainte qui refuse toutes les ressources dont le nom correspond à une valeur fournie par le créateur de la contrainte. Le reste de cette page traite du contenu du modèle et met en avant des concepts importants.

Si vous utilisez Config Sync avec un dépôt hiérarchique, nous vous recommandons de créer vos contraintes dans le répertoire cluster/.

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8sdenyname
spec:
  crd:
    spec:
      names:
        kind: K8sDenyName
      validation:
        # Schema for the `parameters` field
        openAPIV3Schema:
          properties:
            invalidName:
              type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sdenynames
        violation[{"msg": msg}] {
          input.review.object.metadata.name == input.parameters.invalidName
          msg := sprintf("The name %v is not allowed", [input.parameters.invalidName])
        }

Exemple de contrainte

Voici un exemple de contrainte que vous pouvez mettre en œuvre pour refuser toutes les ressources nommées policy-violation :

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDenyName
metadata:
  name: no-policy-violation
spec:
  parameters:
    invalidName: "policy-violation"

Composantes d'un modèle de contrainte

Les modèles de contrainte comportent deux éléments importants :

  • Le schéma de la contrainte que vous souhaitez que les utilisateurs créent. Le schéma d'un modèle de contrainte est stocké dans le champ crd.

  • Le code source Rego qui est exécuté lorsque la contrainte est évaluée. Le code source Rego d'un modèle est stocké dans le champ targets.

Schéma (champ crd)

Le champ CRD est un modèle de création de la définition de ressource personnalisée Kubernetes qui définit la ressource de contrainte pour le serveur d'API Kubernetes. Il vous suffit de remplir les champs suivants.

Champ Description
spec.crd.spec.names.kind Le genre de la contrainte. Lorsque ce champ est en minuscule, sa valeur doit être égale à metadata.name.
spec.crd.spec.validation.openAPIV3Schema

Schéma du champ spec.parameters de la ressource de contrainte (Policy Controller définit automatiquement le reste du schéma de la contrainte). Il suit les mêmes conventions que dans une ressource CRD standard.

Préfixer le modèle de contrainte avec le nom K8s est une convention qui vous permet d'éviter les conflits avec d'autres types de modèles de contrainte, tels que les modèles Forseti qui ciblent les ressources Google Cloud.

Code source Rego (champ targets)

Les sections suivantes vous fournissent plus d'informations sur le code source Rego.

Zone

Le code source Rego est stocké dans le champ spec.targets, où targets est un tableau d'objets au format suivant :

{"target": "admission.k8s.gatekeeper.sh","rego": REGO_SOURCE_CODE, "libs": LIST_OF_REGO_LIBRARIES}
  • target : indique à Policy Controller le système que nous examinons (dans le cas présent, Kubernetes). Une seule entrée est autorisée dans targets.
  • rego : code source de la contrainte
  • libs : liste facultative de bibliothèques de code Rego mises à la disposition du modèle de contrainte. Il est conçu pour faciliter l'utilisation des bibliothèques partagées.

Provenance

Voici le code source Rego pour la contrainte précédente :

package k8sdenynames

violation[{"msg": msg}] {
   input.review.object.metadata.name == input.parameters.invalidName
   msg := sprintf("The name %v is not allowed", [input.parameters.invalidName])
}

Veuillez noter les points suivants :

  • package k8sdenynames est requis par OPA (environnement d'exécution de Rego). La valeur est ignorée.
  • La règle Rego que Policy Controller invoque pour voir s'il y a des violations est nommée violation. Si cette règle a des correspondances, une violation de la contrainte s'est produite.
  • La règle violation possède la signature violation[{"msg": "violation message for the user"}], où la valeur de "msg" est le message de non-respect renvoyé à l'utilisateur.
  • Les paramètres fournis à la contrainte sont mis à disposition sous le mot clé input.parameters.
  • La valeur request-under-test est stockée sous le mot clé input.review.

Le mot clé input.review comporte les champs suivants.

Champ Description
uid Identifiant unique de cette requête. Il n'est pas disponible pendant l'audit.
kind

Informations sur le genre de object-under-test. Son format est le suivant :

  • kind : genre de la ressource
  • group : groupe de la ressource
  • version : version de la ressource
name Nom de la ressource Il peut être vide si l'utilisateur s'appuie sur le serveur d'API pour générer le nom sur une requête CREATE.
namespace Espace de noms de la ressource (non fourni pour les ressources à l'échelle d'un cluster)
operation Opération demandée (par exemple, CREATE ou UPDATE). Il n'est pas disponible pendant l'audit.
userInfo

Les informations de l'utilisateur demandeur. Il n'est pas disponible pendant l'audit. Son format est le suivant :

  • username : l'utilisateur qui effectue la requête
  • uid : ID de l'utilisateur
  • groups : liste des groupes dont l'utilisateur est membre
  • extra : toute information supplémentaire fournie par Kubernetes
object Objet que l'utilisateur tente de modifier ou de créer.
oldObject État d'origine de l'objet. Il n'est disponible que pour les opérations UPDATE.
dryRun Indique si cette requête a été appelée avec kubectl --dry-run. Il n'est pas disponible pendant l'audit.

Écrire des modèles de contraintes référentielles

Les modèles de contraintes référentielles sont des modèles qui permettent à l'utilisateur de contraindre un objet par rapport à d'autres objets. Par exemple, "ne pas autoriser la création d'un pod avant qu'une entrée correspondante ne soit connue", ou "ne pas autoriser deux services à avoir le même nom d'hôte".

Policy Controller vous permet d'écrire des contraintes référentielles en surveillant le serveur d'API vis-à-vis d'un ensemble de ressources fournies par l'utilisateur. Lorsqu'une ressource est modifiée, Policy Controller la met en cache localement afin qu'elle puisse être facilement référencée par le code source Rego. Policy Controller rend ce cache disponible sous le mot clé data.inventory.

Les ressources à l'échelle d'un cluster sont mises en cache à l'emplacement suivant :

data.inventory.cluster["GROUP_VERSION"]["KIND"]["NAME"]

Par exemple, un nœud nommé my-favorite-node peut se trouver sous

data.inventory.cluster["v1"]["Node"]["my-favorite-node"]

Les ressources à l'échelle de l'espace de noms sont mises en cache ici :

data.inventory.namespace["NAMESPACE"]["GROUP_VERSION"]["KIND"]["NAME"]

Par exemple, un ConfigMap nommé production-variables dans l'espace de noms shipping-prod peut se trouver sous :

data.inventory.namespace["shipping-prod"]["v1"]["ConfigMap"]["production-variables"]

Le contenu complet de l'objet est stocké à cet emplacement de cache et peut être référencé dans votre code source Rego comme bon vous semble.

En savoir plus sur Rego

Les informations ci-dessus fournissent les fonctionnalités uniques de Policy Controller qui facilitent l'écriture de contraintes sur les ressources Kubernetes dans Rego. Un tutoriel complet sur la façon d'écrire dans Rego est hors du champ d'application de ce guide. Cependant, la documentation d'Open Policy Agent contient des informations sur la syntaxe et les fonctionnalités du langage Rego lui-même.

Installer votre modèle de contrainte

Une fois que vous avez créé votre modèle de contrainte, utilisez kubectl apply pour l'appliquer. Policy Controller se charge de l'ingérer. Veillez à vérifier le champ status de votre modèle de contrainte pour vous assurer qu'il n'y a pas eu d'erreur lors de son instanciation. Si l'ingestion réussie, le champ status doit afficher created: true et le résultat de observedGeneration noté dans le champ status doit être égal au champ metadata.generation.

Une fois le modèle ingéré, vous pouvez lui appliquer des contraintes, comme décrit dans la section Créer des contraintes.

Supprimer un modèle de contrainte

Pour supprimer un modèle de contrainte, procédez comme suit :

  1. Vérifiez qu'aucune contrainte que vous souhaitez conserver n'utilise le modèle de contrainte :

    kubectl get TEMPLATE_NAME
    

    En cas de conflit de noms entre le nom du modèle de contrainte et un autre objet du cluster, exécutez plutôt la commande suivante :

    kubectl get TEMPLATE_NAME.constraints.gatekeeper.sh
    
  2. Supprimez le modèle de contrainte :

    kubectl delete constrainttemplate CONSTRAINT_TEMPLATE_NAME
    

Lorsque vous supprimez un modèle de contrainte, vous ne pouvez plus créer de contraintes qui le référencent.

Étape suivante