Sunday, December 15, 2019

Tip:OPA gatekeeper REGO nodeSelector Constraint Template

Symptom:

    We start to use OPA gatekeeper for our kubernetes clusters. Refer https://github.com/open-policy-agent/gatekeeper
    We try to enforce all pods and deployment...etc to have a assigned nodeSelector.  We had some issues. The details of the issue can be found in github link

Solutions:

Rego template is like this:
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8sallowednodeselector
spec:
  crd:
    spec:
      names:
        kind: K8sAllowedNodeselector
        listKind: K8sAllowedNodeselectorList
        plural: k8sallowednodeselector
        singular: k8sallowednodeselector
      validation:
        # Schema for the `parameters` field
        openAPIV3Schema:
          properties:
            labels:
              type: array
              items:
                type: object
                properties:
                  key:
                    type: string
                  allowedvalue:
                    type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sallowednodeselector
        key1 := { k | input.review.object.spec.nodeSelector[k] }
        key2 := { k | input.review.object.spec.template.spec.nodeSelector[k]  }
        mykey := key1 | key2

        # Make sure all required selectors are implemented in template including deployment, replicaset,sts...
        violation[{"msg": msg}] {
          provided := mykey
          required := {label | label := input.parameters.labels[_].key}
          missing := required - provided
          expected :=  input.parameters.labels[_]
          count(missing) > 0
          msg := sprintf("Missing nodeSelector label <%v: %v>, or too many nodeSelector labels,only 1 nodeSelector lable is allowed.",[expected.key,expected.allowedvalue])
        }
        #Make sure that ONLY required selectors are used
        violation[{"msg": msg}] {
          provided := mykey
          required := {label | label := input.parameters.labels[_].key}
          missing := provided - required
          expected :=  input.parameters.labels[_]
          count(missing) > 0
          msg := sprintf("Missing nodeSelector label <%v: %v>, or too many nodeSelector labels,only 1 nodeSelector lable is allowed.",[expected.key,expected.allowedvalue])
        }
        #Make sure all required selectors are implemented in template including deployment, replicaset,sts...
        violation[{"msg": msg}] {
          value :=  input.review.object.spec.template.spec.nodeSelector[key]
          expected :=  input.parameters.labels[_]
          expected.key == key
          not  expected.allowedvalue == value
          msg := sprintf("Value in Label <%v: %v> does not satisfy allowed value:<%v: %v>", [key,value,expected.key,expected.allowedvalue])
        }
        #Make sure all required selectors are implemented in pod
        violation[{"msg": msg}] {
          value :=  input.review.object.spec.nodeSelector[key]
          expected :=  input.parameters.labels[_]
          expected.key == key
          not  expected.allowedvalue == value
          msg := sprintf("nodSelector of Pod <%v: %v> does not satisfy allowed value:<%v: %v>", [key,value,expected.key,expected.allowedvalue])
        }

No comments: