-
Notifications
You must be signed in to change notification settings - Fork 8
Gatekeeper
OPA Gatekeeper defines policies in Rego as well. However, Gatekeeper is focused on Kubernetes, and takes advantage of a custom resource definition to specify policies:
-
ConstraintTemplate
: this custom resource definition defines a policy body (in the Rego language), the name it can be instantiated with (Gatekeeper will generate a CRD with this name), a descriptive name for the constraint template itself, and an optional OpenAPI v3 schema that defines what properties can be configured on it when invoking it.
Gatekeeper policies work by returning a list of violations
based on
the object to be reviewed and the passed parameters.
-
Gatekeeper includes a Rego library that includes many helper definitions. This definitions are originally found here.
-
The OPA constraint framework is what is used by Gatekeeper to initialize the policy, and includes the previous Rego library.
The ConstraintTemplate
creates the CRD, validates and then
rewrites the Rego
sources
specified in the ConstraintTemplate
custom resource.
The following policy has a descriptive name k8srequiredlabels
. It
will make Gatekeeper define a CRD with kind K8sRequiredLabels
(the
name on .spec.crd.spec.names.kind
), and apiVersion
constraints.gatekeeper.sh/v1beta1
that can be used to instantiate
the policy as many times as we need inside a cluster.
Let's define the policy by creating a ConstraintTemplate
with API
version templates.gatekeeper.sh/v1beta1
first:
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])
}
After having defined our policy, we can instantiate it using the
kind
we used on the ConstraintTemplate
to define the template in
the earlier step and API version constraints.gatekeeper.sh/v1beta1
:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: all-must-have-owner
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
message: "All namespaces must have an `owner` label that points to your company username"
labels:
- key: owner
allowedRegex: "^[a-zA-Z]+.agilebank.demo$"
For every defined policy with ConstraintTemplate
kind, a new CRD is
generated that can be instantiated, and that represents the policy
itself, with the given parameters in the instantiation itself.
Gatekeeper policies have a toplevel input
object, that is built as
follows -- represented as JSON:
{
"input": {
"review": {
<Kubernetes AdmissionRequest to be reviewed in JSON format>
},
"parameters": {
<properties defined in the OpenAPI v3 schema of the ConstraintTemplate in JSON format>
}
}
}
Outputs in Gatekeeper are formed by a list of violations
. If the
list of violations
is empty, it is assumed that the request is
accepted.
A violation
might contain a msg
attribute, such as:
violation[{"msg": msg, "details": {}}] {
value := input.review.object.metadata.namespace
value == "default"
msg := sprintf("Namespace should not be default: %v", [value])
}
The msg
is a free form text message that will be returned to the API
server, while the details
attribute represents an object that might
contain arbitrary content about the rejection and values that lead to
the rejection of the request. Example:
violation[{"msg": "some labels are missing"}] {