Skip to content

Gatekeeper

Rafael Fernández López edited this page Aug 27, 2021 · 4 revisions

OPA 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.

The ConstraintTemplate creates the CRD, validates and then rewrites the Rego sources specified in the ConstraintTemplate custom resource.

Defining a policy

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])
        }

Instantiating a policy

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.

Inputs

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

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"}] {
Clone this wiki locally