carrotcakeslayer
carrotcakeslayer

Reputation: 1008

Rego - Looping through a list of lists to check if they match given parameters

We currently can control which Kubernetes services of type "LoadBalancer" can be created.

ConstraintTemplate

spec:
  crd:
    spec:
      names:
        kind: K8sMetalLBServicesGK
      validation:
        openAPIV3Schema:
          properties:
            allowedLBServices:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8smetallbservicesgk

        # Deny not whitelisted Service LB names
        violation[{"msg": msg}] {
          is_not_getting_deleted
          is_type_loadbalancer
          svc := input.review.object.metadata.name
          satisfied := [good | pattern := input.parameters.allowedLBServices[_] ; good = glob.match(pattern, [], svc)]
          not any(satisfied)
          msg := sprintf("%v" service creation was denied by GateKeeper,[svc])
        }
        
        is_not_getting_deleted() = true {
          input.review.operation != "DELETE"
          not input.review.object.metadata.deletionTimestamp
        }

        is_type_loadbalancer() = true {
          input.review.object.spec.type == "LoadBalancer"
        }

Constraint

spec:
  match:
    excludedNamespaces: ["*-system"]
    kinds:
      - apiGroups: ["*"]
        kinds: ["Service"]
  parameters:
    allowedLBServices:
      - pakalu

We would like to extend this by controlling which service of type "LoadBalancer" can be created per namespace. For that, we would need to change the validation in the template and the rule logic. So constraint could look similar to this, if we want to allow services named "pakalu" and "papito" in namespace "ham", and allow services named "pakalu" and "foo" in namespace "spam".

spec:
  match:
    excludedNamespaces: ["*-system"]
    kinds:
      - apiGroups: ["*"]
        kinds: ["Service"]
  parameters:
    allowedLBServices:
      - ham:
          - pakalu
          - papito
      - spam:
          - pakalu
          - foo

Upvotes: 0

Views: 1163

Answers (1)

Devoops
Devoops

Reputation: 2315

You could modify the constraint template to expect an object for allowedLBServices, and modify the policy to work with that:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8smetallbservicesgk
spec:
  crd:
    spec:
      names:
        kind: K8sMetalLBServicesGK
      validation:
        openAPIV3Schema:
          type: object
          properties:
            allowedLBServices:
              type: object

  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8smetallbservicesgk

        # Deny not whitelisted Service LB names
        violation[{"msg": msg}] {
          is_not_getting_deleted
          is_type_loadbalancer
          not allowed_in_namespace

          msg := sprintf("%v service creation was denied by GateKeeper", [input.review.object.metadata.name])
        }

        allowed_in_namespace {
          namespace := input.review.object.metadata.namespace
          pattern := input.parameters.allowedLBServices[namespace][_]
          glob.match(pattern, [], input.review.object.metadata.name)
        }

        is_not_getting_deleted {
          input.review.operation != "DELETE"
          not input.review.object.metadata.deletionTimestamp
        }

        is_type_loadbalancer {
          input.review.object.spec.type == "LoadBalancer"
        }

And update the constraint to use an object as well:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sMetalLBServicesGK
metadata:
  name: foo
spec:
  match:
    excludedNamespaces: ["*-system"]
    kinds:
      - apiGroups: ["*"]
        kinds: ["Service"]
  parameters:
    allowedLBServices:
      foo:
        - pakalu

Nit: I'd use rules over functions, unless you want to provide arguments.

Upvotes: 1

Related Questions