Writing Your First Kubernetes OPA Policy With Gatekeeper

Michael Levan - Sep 14 '22 - - Dev Community

In a previous blog post which you can find here, OPA was explained in the sense of why it’s important and how it changes the game for policy management in today’s world.

Now that you know the theory from that blog post, let’s talk about how to get hands-on.

In this blog post, you’ll learn about installing Gatekeeper and how to configure policies for your Kubernetes cluster.

Gatekeeper Install

First, you’ll need to install Gatekeeper on your Kubernetes cluster. To do that, you can use Helm.

Add the Gatekeeper repo.

helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
Enter fullscreen mode Exit fullscreen mode

Next, run the installation.

helm install gatekeeper/gatekeeper --name-template=gatekeeper --namespace gatekeeper-system --create-namespace
Enter fullscreen mode Exit fullscreen mode

You should see an output similar to the one below.

NAME: gatekeeper
LAST DEPLOYED: Thu Sep  8 12:36:20 2022
NAMESPACE: gatekeeper-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
Enter fullscreen mode Exit fullscreen mode

Config Implementation

Once Gatekeeper is installed, you’ll have to configure it. There are three types of configurations:

  • The config for Gatekeeper itself
  • The template
  • The constraint implementation

Config

The config is your definition of what Gatekeeper is allowed to create policies for. For example, in the config.yaml below, Gatekeeper knows that it can only specify Policies for Pods, but no other Kubernetes resources.

apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
  name: config
  namespace: "gatekeeper-system"
spec:
  sync:
    syncOnly:
      - group: ""
        version: "v1"
        kind: "Pod"
Enter fullscreen mode Exit fullscreen mode

The config also ensures that the policy is set across clusters, not just the node where Gatekeeper was installed.

For example, if you have Pods running on multiple worker nodes, you'd need a Config so Gatekeeper can have access to the Pods across worker nodes.

Constraint Template

The Constraint Template is the rule/policy that you want to configure for your environment. It's a template, so you can use it across multiple constraints.

For example, there's a Rego policy in the constraint template in this directory that ensures no one can utilize the latest tag of a container image.

Copy the below constraint template and add it to a YAML file. Once complete, run kubectl apply -f on the YAML file.

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: blocklatesttag
  annotations:
    description: Blocks container images from using the latest tag
spec:
  crd:
    spec:
      names:
        kind: blocklatesttag # this must be the same name as the name on metadata.name (line 4)
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package blocklatesttag
        violation[{"msg": msg, "details": {}}]{
        input.review.object.kind == "Pod"
        imagename := input.review.object.spec.containers[_].image
        endswith(imagename,"latest")
        msg := "Images with tag the tag \"latest\" is not allowed"
        }
Enter fullscreen mode Exit fullscreen mode

Constraint Config

The constraint itself is taking the template that you created above and bringing it to life. It allows you to utilize the template to create your own policy inside of a Kubernetes cluster.

Copy the below constraint template and add it to a YAML file. Once complete, run kubectl apply -f on the YAML file.

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: blocklatesttag
metadata:
  name: nolatestcontainerimage
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
  parameters:
    annotation: "no-latest-tag-used"
Enter fullscreen mode Exit fullscreen mode

Testing The Config

To confirm that the OPA policy that you implemented works, you can test it out with the two Kubernetes Manifests below.

The Manifest with the latest tag won’t work because you created a policy in the previous step to ensure that latest tags cannot be used. The deployment itself will deploy, but the Pods won’t come online.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        ports:
        - containerPort: 80
Enter fullscreen mode Exit fullscreen mode

The Manifest below will work and the Pods will come online because a container image version number is specified.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:1.23.1
        ports:
        - containerPort: 80
Enter fullscreen mode Exit fullscreen mode

Congrats! You have successfully set up a full OPA integration.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .