OPA est open source et est un moteur de politiques (policy) polyvalent qui unifie l'application des politiques dans les stack. OPA fournit un langage déclaratif de haut niveau qui vous permet de spécifier la politique en tant que code (Policy as Code) et des API simples. Vous pouvez utiliser OPA pour appliquer des politiques dans les microservices, Kubernetes, les pipelines CI/CD, les passerelles API, etc.
Dans K8S, OPA utilise l'Admission Controller
, pour rappel :
Le controller OPA (OPA Gatekeeper) créer de nouvelles ressources (CRD) dans Kubernetes.
Exemple :
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: pod-must-have-label-x
Ici on peut imaginer que ce template vérifie que la ressource ait bien un label.
Rien de plus simpla, apply le fichier suivant :
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.8/deploy/gatekeeper.yaml
Puis on va voir toutes les ressources custom (CRD) qu'OPA a crée :
root@cks-master:~$ k get crd
NAME CREATED AT
assign.mutations.gatekeeper.sh 2022-07-02T13:37:03Z
assignmetadata.mutations.gatekeeper.sh 2022-07-02T13:37:03Z
configs.config.gatekeeper.sh 2022-07-02T13:37:03Z
constraintpodstatuses.status.gatekeeper.sh 2022-07-02T13:37:03Z
constrainttemplatepodstatuses.status.gatekeeper.sh 2022-07-02T13:37:03Z
constrainttemplates.templates.gatekeeper.sh 2022-07-02T13:37:03Z
modifyset.mutations.gatekeeper.sh 2022-07-02T13:37:03Z
mutatorpodstatuses.status.gatekeeper.sh 2022-07-02T13:37:03Z
providers.externaldata.gatekeeper.sh 2022-07-02T13:37:03Z
Nous allons d'abord créer une ConstraintTemplate
:
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8salwaysdeny
spec:
crd:
spec:
names:
# va créer une ressource cusom "K8sAlwaysDeny"
kind: K8sAlwaysDeny
validation:
# Schema for the `parameters` field
openAPIV3Schema:
properties:
message:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
# Ecrit en "Rego" (langage)
rego: |
package k8salwaysdeny
violation[{"msg": msg}] {
1 > 0 # La condition est vraie donc on entre dans la violation
msg := input.parameters.message
}
Puis la contrainte :
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAlwaysDeny
metadata:
name: pod-always-deny
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
message: "ACCESS DENIED!"
Une fois appliquée :
root@cks-master:~$ k get crd | grep alw
k8salwaysdeny.constraints.gatekeeper.sh 2022-07-02T13:49:07Z
root@cks-master:~$ k get k8salwaysdeny
NAME ENFORCEMENT-ACTION TOTAL-VIOLATIONS
pod-always-deny
root@cks-master:~$ k run pod --image=nginx
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [pod-always-deny] ACCESS DENIED!
root@cks-master:~$ k get k8salwaysdeny
NAME ENFORCEMENT-ACTION TOTAL-VIOLATIONS
pod-always-deny 19
Pour autoriser de nouveau tous les pods, ajouter une condition dans la ConstraintTemplate
, par exemple 1 > 2
, la condition renvoyant false, la violation ne sera pas envoyée.
ConstraintTemplate
:
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: 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])
}
Constraint
:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-cks
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels: ["cks"]
Puis :
root@cks-master:~$ k create ns test
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [ns-must-have-cks] you must provide labels: {"cks"}
Il faut donc avoir un namespace avec le label cks
comme écrit dans notre contrainte.
ConstraintTemplate
:
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8sminreplicacount
spec:
crd:
spec:
names:
kind: K8sMinReplicaCount
validation:
# Schema for the `parameters` field
openAPIV3Schema:
properties:
min:
type: integer
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sminreplicacount
violation[{"msg": msg, "details": {"missing_replicas": missing}}] {
provided := input.review.object.spec.replicas
required := input.parameters.min
missing := required - provided
missing > 0
msg := sprintf("you must provide %v more replicas", [missing])
}
Constraint
:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sMinReplicaCount
metadata:
name: deployment-must-have-min-replicas
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
parameters:
min: 2
Il est possible de tester son code Rego avant de l'appliquer dans le cluster Kubernetes. Pour se faire aller sur le site suivant : https://play.openpolicyagent.org