Dans mes articles précédents, nous avons expliqué ce qu’est Policy-as-Code, pourquoi nous en avons besoin et comment utiliser l’outil Open Policy Agent (OPA). Si vous n’avez pas encore lu l’introduction, veuillez d’abord prendre le temps de la lire ici.
Après l’introduction d’OPA, j’ai commencé la première partie d’un tutoriel d’intégration OPA/Kubernetes, montrant comment utiliser OPA pour appliquer des politiques dans un cluster Kubernetes. Voici le lien vers la première partie du tutoriel.
Présentation du gardien
L’OPA Gatekeeper est un projet sous l’égide de l’OPA. Bien que je n’ai pas mentionné Gatekeeper dans le tutoriel précédent, la technique que j’y ai décrite (utilisant OPA avec son sidecar kube-mgmt
) est également appelé Gardien v1.0.
Aujourd’hui, nous allons nous familiariser avec Gardien v3 (ci-après : simplement « Gatekeeper », en omettant la partie « v3 »), qui construit un contrôleur d’admission Kubernetes autour du moteur de politique pour intégrer OPA et le service d’API Kubernetes.
Politiques basées sur CRD
La valeur la plus importante de Gatekeeper est la possibilité de configurer dynamiquement des politiques OPA à l’aide de Gatekeeper. Définitions de ressources personnalisées (CRD). Les ressources personnalisées sont des extensions de l’API Kubernetes qui permettent de personnaliser une installation Kubernetes.
Les politiques basées sur CRD permettent une intégration plus profonde d’OPA dans l’écosystème Kubernetes : elles permettent la création de modèles de politiques pour les politiques Rego, la création de politiques en tant que CRD et le stockage des résultats d’audit sur les politiques CRD.
En quoi Gatekeeper est-il différent d’OPA ?
Par rapport à l’utilisation d’OPA avec son side-car kube-mgmt
(alias Gatekeeper v1.0), Gatekeeper introduit les fonctionnalités suivantes :
- Une bibliothèque de politiques extensible et paramétrable.
- CRD Kubernetes natifs pour l’instanciation de la bibliothèque de politiques (alias « contraintes »)
- CRD Kubernetes natifs pour étendre la bibliothèque de stratégies (alias « modèles de contraintes »).
- CRD Kubernetes natifs pour la prise en charge des mutations.
- Fonctionnalité d’audit.
- Prise en charge des données externes.
Cela peut sembler un peu trop abstrait, alors passons aux détails du fonctionnement de Gatekeeper.
Installer le garde-porte
D’abord, commençons minikube
:
Pour ce tutoriel, nous allons déployer une version publiée de Gatekeeper dans notre cluster minikube avec une image prédéfinie :
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml
Remarque : il est également possible de le déployer avec Helm :
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm install gatekeeper/gatekeeper --name-template=gatekeeper --namespace gatekeeper-system --create-namespace
Après le déploiement, un nouvel espace de noms gatekeeper-system
seront créés, et les ressources suivantes seront créées :
tiexin@mbp ~ $ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
gatekeeper-audit 1/1 1 1 2m20s
gatekeeper-controller-manager 3/3 3 3 2m20s
tiexin@mbp ~ $ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gatekeeper-webhook-service ClusterIP 10.103.86.204 <none> 443/TCP 2m22s
tiexin@mbp ~ $ kubectl get crd
NAME CREATED AT
assign.mutations. Gatekeeper.sh 2023-01-01T08:59:54Z
assignmetadata.mutations.gatekeeper.sh 2023-01-01T08:59:54Z
configs.config.gatekeeper.sh 2023-01-01T08:59:55Z
constraintpodstatuses.status.gatekeeper.sh 2023-01-01T08:59:55Z
constrainttemplatepodstatuses.status.gatekeeper.sh 2023-01-01T08:59:55Z
constrainttemplates.templates.gatekeeper.sh 2023-01-01T08:59:55Z
expansiontemplate.expansion. Gatekeeper.sh 2023-01-01T08:59:55Z
modifyset.mutations. Gatekeeper.sh 2023-01-01T08:59:55Z
mutatorpodstatuses.status. Gatekeeper.sh 2023-01-01T08:59:55Z
providers.externaldata. Gatekeeper.sh 2023-01-01T08:59:55Z
Concepts Gatekeeper : Contraintes et modèles de contraintes
Avant de passer au didacticiel proprement dit, examinons deux concepts essentiels de Gatekeeper avec des exemples concrets : contraintes et modèles de contraintes.
En bref, les contraintes utilisent des modèles de contraintes pour informer Gatekeeper des politiques à appliquer et comment.
Je sais que cela semble un peu déroutant, alors regardons un exemple :
Le ConstraintTemplate
L’exemple ci-dessous contient du code Rego, qui vérifie si un objet de ressource a une étiquette nommée « team : »
Déposer constraint_template.yaml
:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: teamlabel
spec:
crd:
spec:
names:
kind: TeamLabel
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package teamlabel
labels := input.review.object.metadata.labels
has_team {
labels.team
}
violation[{"msg": msg}] {
not has_team
msg := "You should have the team label"
}
Le ConstraintTemplate
L’objet ci-dessus ne déclenche pas l’application de la stratégie par lui-même. Cependant, il crée une nouvelle ressource personnalisée dans notre cluster du type TeamLabel
. Si nous voulons faire respecter notre TeamLabel
policy, nous créons une contrainte en utilisant ce nouveau type de ressource :
Déposer constraint.yaml
:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: TeamLabel
metadata:
name: teampods
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
parameters: {}
Cette contrainte utilise le modèle de contrainte TeamLabel ci-dessus pour permettre à Gatekeeper d’appliquer notre politique TeamLabel pour tous les pods qui ne se trouvent pas dans le kube-system
espace de noms.
Le service Gatekeeper surveille et audite également en permanence et en permanence les objets de cluster existants pour détecter les violations de politique.
Comment utiliser Gatekeeper dans Kubernetes
Créer des fichiers constraint_template.yaml
et constraint.yaml
avec le contenu de la section précédente, et appliquez-les :
kubectl apply -f constraint_template.yaml
kubectl apply -f constraint.yaml
Maintenant, déployons un pod simple dans l’espace de noms par défaut à titre de test :
kubectl apply -n default -f https://k8s.io/examples/pods/simple-pod.yaml
Nous obtiendrons l’erreur suivante :
Error from server (Forbidden): error when creating "https://k8s.io/examples/pods/simple-pod.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [teampods] You should have the team label
Mettons-nous en conformité et ajoutons une étiquette « équipe » au pod simple :
Déposer simple-pod.yaml
:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
team: test
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Créez ce fichier avec le contenu ci-dessus et appliquez-le :
kubectl apply -n default -f simple-pod.yaml
Nous ne devrions rencontrer aucune erreur et le pod sera créé avec succès :
tiexin@mbp ~ $ kubectl get pods -n default
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 34s
Comment faire évoluer l’OPA
Jusqu’à présent, nous avons démontré un cas d’utilisation simple d’intégration OPA/Kubernetes avec Gatekeeper.
Cependant, les politiques peuvent être beaucoup plus compliquées dans des scénarios réels et vous aurez plusieurs politiques. Comment OPA fonctionne-t-il à une échelle beaucoup plus grande ?
Parlons de trois sujets cruciaux sur ce sujet :
- Structure du référentiel
- Politiques de test
- Autres outils
Structure du référentiel
Un répertoire bien structuré aide à rendre votre code plus facile à gérer :
.
└── team-label-policy
├── README.md
├── constraint.yaml
├── constraint_template.yaml
├── simple-pod.yaml
├── src.rego
└── src_test.rego
Dans l’exemple ci-dessus, nous plaçons tout ce qui appartient à la stratégie d’étiquette d’équipe dans le dossier « team-label-policy », qui contient les fichiers principaux :
src.rego
: le code OPA Rego.src_test.rego
: les cas de test correspondants pour notre politique Rego.constraint_template.yaml
:ConstraintTemplate
pour notre politique.- Note: ce fichier contient également le code de
src.rego
inline, mais l’outil OPA ne peut pas analyser le manifeste YAML, nous devons donc déplacer le code Rego vers un fichier séparé pour le test. Si vous utilisez cette mise en page pour vos politiques, vous devez vous rappeler de synchroniser les changements de code entre les deux fichiers.
- Note: ce fichier contient également le code de
constraint.yaml
: le manifeste pour un test du modèle de contrainte.simple-pod.yaml
: une définition de pod minimaliste pour démontrer la contrainte en pratique.README.md
Comment tester une politique de rego
Pour les cas d’utilisation essentiels et les politiques compliquées, nous devons également écrire des tests pour ces politiques.
Lors de la rédaction de la couverture de test pour votre politique Gatekeeper, vous devez examiner attentivement les points suivants :
- Quels champs de ressource d’API Kubernetes interrogent ma stratégie ?
- Certains d’entre eux sont-ils facultatifs ?
- Peuvent-ils apparaître plus d’une fois dans une spécification ?
- Combien de cas de test positifs dois-je écrire pour m’assurer que ma politique fera ce que j’attends ?
- Combien de cas de test négatifs dois-je écrire pour m’assurer que ma politique ne produira pas de résultats que je ne souhaite pas ?
Les tests de politique sont également écrits en Rego.
Par convention, ils résident dans le même répertoire que le fichier source. Dans notre cas, nous pouvons déplacer la politique du constraint_template.yaml
déposer dans src.rego
et écrire des tests dans le fichier src_test.rego
.
Note: le nom du package correspondant en haut de chaque fichier :
Déposer src.rego
:
package teamlabel
# copied from file constraint_template.yaml
labels := input.review.object.metadata.labels
has_team {
labels.team
}
violation[{"msg": msg}] {
not has_team
msg := "You should have the team label"
}
Déposer src_test.rego
:
package teamlabel
import future.keywords
test_pod_allowed if {
results := violation with input as {"review": {"object": {"metadata": {"labels": { "team": "test" }}}}}
...