Kubernetes policy management: IV - jsPolicy
Tags: kubernetes, policy, loft, jspolicy
Published: 2022-03-06 12:48:35

This is the final post of the series.

Other parts:
[I - Introduction](https://dev.to/ashokan/kubernetes-policy-management-through-admission-web-hooks-i-11i2)
[II - OPA Gatekeeper](https://dev.to/ashokan/kubernetes-policy-management-ii-opa-gatekeeper-465g)
[III - Kyverno](https://dev.to/ashokan/kubernetes-policy-management-iii-kyverno-2gjj)

---
**jsPolicy** offers a simple alternative to OPA. You can use it to write policy logic in JavaScript or TypeScript, and it offers Turing completeness, easy cluster access, and a huge ecosystem of libraries. Other features include validating, mutating, or changing policies, and defining controller policies. It uses Chrome V8 engine underneath, so performance and stability are battle tested

**Installation**

```
❯ helm install jspolicy jspolicy -n jspolicy --create-namespace --repo https://charts.loft.sh
NAME: jspolicy
LAST DEPLOYED: Tue Mar  8 20:46:34 2022
NAMESPACE: jspolicy
STATUS: deployed
...
❯ kubectl get crds | grep jspolicy
jspolicies.policy.jspolicy.com           2022-03-08T15:16:28Z
jspolicybundles.policy.jspolicy.com      2022-03-08T15:16:28Z
jspolicyviolations.policy.jspolicy.com   2022-03-08T15:16:28Z

```

**Architecture**
[jsPolicy architecture from loft labs](https://www.jspolicy.com/docs/media/diagrams/jspolicy-architecture.svg)

**Creating and instantiating policies**
Validating policy to disallow workload creation under namespaces `default` and `restricted`
```
❯ cat /tmp/policy.yaml
# policy.yaml
apiVersion: policy.jspolicy.com/v1beta1
kind: JsPolicy
metadata:
  name: "deny-default-namespace.example.com"
spec:
  operations: ["CREATE"]
  resources: ["*"]
  scope: Namespaced
  javascript: |
    var namespacesToDeny = ["default", "restricted"];
    if (namespacesToDeny.includes(request.namespace)) {
      deny("Creation of resources is not allowed in the following namespaces: " + namespacesToDeny.toString());
    }

❯ kubectl create -f /tmp/policy.yaml
jspolicy.policy.jspolicy.com/deny-default-namespace created
```

Mutating policy to add a `foo=bar` label to pods
```
❯ cat /tmp/mutate.yaml
apiVersion: policy.jspolicy.com/v1beta1
kind: JsPolicy
metadata:
  name: "create-foo-bar-label.example.com"
spec:
  type: Mutating
  operations: ["CREATE", "UPDATE"]
  resources: ["pods"]
  javascript: |
    if (request.object.metadata.labels?.["foo"] !== "bar") {
      request.object.metadata.labels = {...request.object.metadata.labels, 'foo': 'bar'};

      // tell jspolicy to calc the patch and exit
      mutate(request.object);
    }

❯ kubectl create -f /tmp/mutate.yaml
jspolicy.policy.jspolicy.com/create-foo-bar-label.example.com created
❯ kubectl run test-pod --image=nginx -n test-ns
pod/test-pod created
❯ kubectl get pod -n test-ns --show-labels
NAME       READY   STATUS              RESTARTS   AGE   LABELS
test-pod   1/1     Running             0          14s   foo=bar,run=test-pod

```
**Testing**
```
❯ kubectl run test-pod --image=nginx --restart=Never
Error from server (Forbidden): admission webhook "deny-default-namespace.example.com" denied the request: Creation of resources is not allowed in the following namespaces: default,restricted
```

**Extra tools**
Policies created for jsPolicy are regular javascript files =>
- You can develop policies with your favorite editor
- You can test(unit, functional, scale) with any javascript test frameworks like Jest, Mocha ..
- You can write policies as external JavaScript files and load them as a dependency
This is a programming library so it can be scaled, evolved and maintained with relative ease over time.

**More info**
[jsPolicy configuration](https://www.jspolicy.com/docs/writing-policies/configuration)
[jsPolicy custom javascript functions](https://www.jspolicy.com/docs/functions/allow)
[Comparison with OPA](https://loft.sh/blog/kubernetes-policy-enforcement-opa-vs-jspolicy/)

---
I love the rationale(improve understandability) behind JSPolicy