Generate polices ! Make Kubernetes life hassle free with Kyverno
Published: 2020-07-30 02:03:47



Briefly, Kyverno is a policy engine built for Kubernetes. It runs as an admission controller that can mutate and validate incoming resources. Also, Kyverno can generate any type of resource-based on various triggers of the admission request.

<img src="https://evalsocket.dev/kyverno.png" alt="kyverno" width="90%" style="display: block;margin: 0 auto;"/>

A Kyverno generates resources based on a given set of conditions or triggers (read more about match/exclude). Kyverno generates a policy that supports two types of generate rules.

Here is an example of a generated policy

1. Copy a resource from one namespace to another namespace. It's similar to copy command in a shell. The user has to define the `clone` block for providing the source resource path. In the below example source is `config-template` configmap in the default namespace
```yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: basic-policy
spec:
  rules:
    - name: "Generate ConfigMap"
      match:
        resources:
          kinds:
          - Namespace
      generate:
        kind: ConfigMap # Kind of resource
        name: default-config # Name of the new Resource
        namespace: "{{request.object.metadata.name}}" # namespace that triggers this rule
        synchronize : true
        clone:
          namespace: default
          name: config-template
```
2. Generate a resource from inline data. In this type of generating policy users can define a defined data block directly in the policy.
```yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: basic-policy
spec:
  rules:
    - name: "Generate Secret (insecure)"
      match:
        resources:
          kinds:
          - Namespace
      generate:
        kind: ConfigMap
        name: config-app
        namespace: "{{request.object.metadata.name}}" # namespace that triggers this rule
        data:
          data:
            USERNAME: evalsocket=
            DATABASE: blog
        metadata:
          labels:
            purpose: config
```

Both examples above will trigger when a new namespace is created then kyverno will automatically generate the configmap and secrets in the newly created namespace.

```shell
# Create a configmap in default namespace (We need this configmap for generating resources)
➜  evalsocket ✗ kubectl create configmap config-template -n default --from-literal=special.how=very
configmap/config-template created
➜  evalsocket ✗ kubectl apply -f namespace-configmap-secret.yaml
clusterpolicy.kyverno.io/basic-policy created
# Trigger the policy by creating a namespace
➜  evalsocket ✗ kubectl create namespace kyverno-example
namespace/kyverno-example created
#Verify the resources
➜  evalsocket ✗ kubectl get configmap,secret -n kyverno-example
NAME                       DATA   AGE
configmap/default-config   1      42s
configmap/config-app       1      42s

NAME                         TYPE                                  DATA   AGE
secret/default-token-rq4dx   kubernetes.io/service-account-token   3      42s
# We did it
```
Now let's discuss the behavior of the field `synchronize: true`. If you enable synchronization that means that kyverno will manage your generated resources and administrators can only update/delete a resource from the policy. All direct action on generated resources will be blocked by the kyverno. And in case of clone rule, the cluster-admin can update/delete generated resources from the source resources.

```shell
# Let's try to delete configmap generated from default namespace. It should block your request basecause synchronize : true in case of generated configmap
➜  evalsocket ✗  kubectl delete configmap default-config -n kyverno-example
Error from server: admission webhook "nirmata.kyverno.resource.validating-webhook" denied the request: Resource is managed by a Kyverno policy and cannot be update manually. You can edit the generate policy to update this resource.

#Now let's try to delete generated resource configmap that doesn't have synchronize : true and synchronize has default value false.
➜  evalsocket ✗  kubectl delete configmap config-app -n kyverno-example
configmap "config-app" deleted

# Update Your configmap in default namespace  and check your update to see synchronize. Set special.how=why
➜  evalsocket ✗ kubectl edit configmap config-template -n default
configmap/config-template edited

➜  evalsocket ✗ kubectl get configmap default-config -n kyverno-example  -oyaml
apiVersion: v1
data:
  special.how: why
kind: ConfigMap
metadata:
  creationTimestamp: "2020-07-28T00:13:58Z"
  labels:
    app.kubernetes.io/managed-by: kyverno
    app.kubernetes.io/synchronize: enable
    kyverno.io/generated-by: Namespace--kyverno-example
  name: default-config
  namespace: kyverno-example
  resourceVersion: "1682"
  selfLink: /api/v1/namespaces/kyverno-example/configmaps/default-config
  uid: b683b4c1-c498-4c25-b018-aa0c63cbcf7f

```


Let's try one more example. This time we will try to generate rbac from generate policy. By default kyverno service account doesn't have that permission, Admin has to manually provide access to kyverno service account. Please [read](https://github.com/nirmata/kyverno/blob/master/documentation/installation.md#3-configure-kyverno-role) more about kyverno role

Add admin privilege to kyverno for creating the role

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kyverno:policycontroller
rules:
  - apiGroups:
      - '*'
    resources:
      - '*'
    verbs:
      - create
      - delete
      - get
      - list
      - patch
      - update
      - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kyverno:userinfo
rules:
  - apiGroups:
      - '*'
    resources:
      - roles
      - clusterroles
      - rolebindings
      - clusterrolebindings
      - configmaps
    verbs:
      - create
      - delete
      - get
      - list
      - patch
      - update
      - watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: kyverno-admin-generate
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kyverno:generatecontroller # clusterRole defined above, to manage generated resources
subjects:
  - kind: ServiceAccount
    name: kyverno-service-account # default kyverno serviceAccount
    namespace: kyverno
```

Now we are ready for generating role and cluster role. let's take an example policy for generating role

```yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: "gen-role-policy"
spec:
  background: false
  rules:
  - name: "gen-role"
    match:
        resources:
          kinds:
            - Namespace
    generate:
        kind: Role
        name: "ns-role"
        namespace: test
        synchronize: true
        data:
          rules:
          - apiGroups: [""]
            resources: ["pods"]
            verbs: ["get", "watch", "list"]
```

Follow the steps to generate roles

```shell
➜  evalsocket ✗  kubectl apply -f gen-role-policy.yaml
clusterpolicy.kyverno.io/gen-role-policy created

➜  evalsocket ✗ kubectl create ns new
namespace/new created

➜  evalsocket ✗ kubectl get Role -n new
```



Some use cases for the generate policy are

1. [Create default Network policies for each namespace](https://github.com/nirmata/kyverno/blob/master/samples/best_practices/add_network_policy.yaml)
2. [Create default resource quotas for each namespace](https://github.com/nirmata/kyverno/blob/master/samples/best_practices/add_ns_quota.yaml)
3. for more example please check [best practices](https://github.com/nirmata/kyverno/tree/master/samples/best_practices)

Check out the Kyverno [GitHub](https://github.com/nirmata/kyverno#documentation) page to learn more about the generate policy and several other features, like validating and deny rule. Find more about kyverno in the previous post [Kubernetes Policy Management with Kyverno.](https://nirmata.com/2019/07/11/managing-kubernetes-configuration-with-policies/).