Learning Open Policy Agent with Tanzu Mission Control Part-4

· 9 min read

This article is part of the Learning Open Policy Agent with Tanzu Mission Control series.

Series

Part 1 : Overview
Part 2 : Playing with TMC's Open Policy Agent
Part 3 : Dissecting TMC's Open Policy Agent
Part 4 : Writing your own OPA policies from TMC < you are here

Introduction

Up to the previous part, we covered how OPA can be used in TMC.

On top of that, TMC offers what's called Custom Policies — a feature for writing your own OPA policies and managing them from the TMC UI.

To close out this series, let's introduce this feature.

Scenario — deny PV creation via StorageClass

We assume the following scenario.

In Kubernetes, creating a PersistentVolume normally flows like this: the developer defines a PersistentVolumeClaim, and the administrator defines the PersistentVolume.

That is Static volume assignment. The original intent was to prevent developers from creating storage on their own.

https://kubernetes.io/docs/concepts/storage/persistent-volumes/#static

However, there is a way to bypass this flow and create volumes dynamically at PersistentVolumeClaim time: StorageClasses.

That is Dynamic volume assignment. Convenient as it is, it has the problem that unlimited rogue storage requests become possible.

https://kubernetes.io/docs/concepts/storage/persistent-volumes/#dynamic https://kubernetes.io/docs/concepts/storage/storage-classes/

Quotas can restrict this, but they are per-Namespace settings, so there's a risk of gaps.

https://kubernetes.io/docs/tasks/administer-cluster/limit-storage-consumption/

So let's try protecting this with OPA.

Creating the OPA policy

First we need to create the OPA policy.

Referring to this post, extract the input information to feed OPA. This time we define a rule at PVC creation: if storageClassName: default — as in the YAML below — is defined, creation is not allowed.

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim2
spec:
  accessModes:
  -  ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: default

With the input in hand, build the policy in the Playground. Here's the result:

https://play.openpolicyagent.org/p/4cKZIuijl2

It's very simple: if a StorageClassName is present in the Input, deny.

Registering the OPA rule in TMC as a Template

Now define this OPA rule in TMC. Select [Templates] in TMC's left pane, then [Create Templates]. [Provide Yaml] is where you define the ConstraintTemplates explained last time. For this scenario, enter the following:

The YAML in the screenshot is:

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: custom-deny-storageclass
spec:
  crd:
    spec:
      names:
        kind: custom-deny-storageclass
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package denystorageclass

        violation[{"msg": msg, "details": {}}] {
          sc = input.review.object.spec.storageClassName
          msg := "Creating PVC with storageClass is not allowed"
        }

As shown, paste the OPA policy built in the previous step under rego. The rest is mostly boilerplate; the key point is giving metadata.name and spec.crd.spec.names.kind values that don't collide with other rules.

Defining the Custom policy

After registering the Template, go to [Assignments] > [Custom] and select the cluster to apply the rule to.

Select the Template you just created and give it a name.

Next, TargetResource selects which resources the rule applies to. This time select PersistentVolumeClaim. Additionally, "Exclude specific namespaces" lets you optionally set Namespaces exempted from this rule. This time we add allowstorageclass=true.

Run Create Policy in this state. Done.

Trying it out

On the target cluster, immediately try creating a PVC that violates the policy. The rule applies nicely and creation is denied:

% kubectl create -f pvc.yaml
Error from server ([denied by tmc.cgp.deny-storageclass] Creating PVC with storageClass is not allowed): error when creating "pvc.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [denied by tmc.cgp.deny-storageclass] Creating PVC with storageClass is not allowed

Let's also try the Namespace exception rule. First create a Namespace and add the allowstorageclass=true label:

% kubectl create ns test
namespace/test created
% kubectl label namespace test allowstorageclass=true
namespace/test labeled

In this Namespace, the PVC creates without issue:

% kubectl create -f pvc.yaml -n test
persistentvolumeclaim/myclaim2 created
% kubectl get pvc -n test
NAME       STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
myclaim2   Bound    pvc-2116cbbd-7fb8-45b7-92cc-cfe43bbc899c   1Gi        RWO            default        6s

Just as expected.

Summary

Part 4 introduced how to write your own OPA policies using TMC. It turned out to be remarkably simple. And through this series, TMC helped us understand OPA much more deeply.