1

We have an undertermined list of items in a resource that need to be checked in case they are using one of the deprecated given paramenters. In gatekeeper, the constraint with the parameters looks like this:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: SOMEKind
metadata:
  name: somename
spec:
  match:
    kinds:
    - apiGroups: ["networking.istio.io"]
      kinds: ["EnvoyFilter"]
  parameters:
    envoyfilters:
    - http_squash:
      canonical: "envoy.filters.http.squash"
      deprecated: "envoy.squash"
    - listener_http_inspector:
      canonical: "envoy.filters.listener.http_inspector"
      deprecated: "envoy.listener.http_inspector"

The resource to check looks like this:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: envoyfilter
spec:
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_OUTBOUND
      listener:
        filterChain:
          filter:
            name: envoy.listener.http_inspector
            subFilter:
              name: envoy.filters.http.router

We would need to make sure than for any .spec.configPatches[].match.listener.filterChain.filter.name that could exist in the analyzed resource, if that name matches any of the given "parameters.envoyfilters[].deprecated given, a violation would happen.

Due to constraints, we are unable to update opa/gk at the moment so we can't use the "import future.keywords".

The reason for having the "canonical" in the list, is because we would like to provide the "good" alertnative fitler to use in the msg of the violation.

We are trying different approaches but this is the latest (not working) one:

      violation[{"msg": msg}] {
        config_patches := input.review.object.spec.configPatches[match][listener][filterchain][filter][_].name
        deprecated_envoyfilters := input.parameters.envoyfilters
        use_deprecated_envoyfilters(config_patches,deprecated_envoyfilters)
        msg := sprintf("REVIEW OBJECT: %v", [config_patches])
      }

      contains(filters, filter) {
        filters[_] = filter
      }

      use_deprecated_envoyfilters(config_patches,deprecated_envoyfilters) = true {
        counter := [ef | efs := config_patches ; contains(efs,deprecated_envoyfilters[_].deprecated) ]
        count(counter) > 0
carrotcakeslayer
  • 809
  • 2
  • 9
  • 33

2 Answers2

1

First of, did you really mean these parameters?

  parameters:
    envoyfilters:
    - http_squash:
      canonical: "envoy.filters.http.squash"
      deprecated: "envoy.squash"
    - listener_http_inspector:
      canonical: "envoy.filters.listener.http_inspector"
      deprecated: "envoy.listener.http_inspector"

because they translate to, for example,

{
  "http_squash": null,
  "canonical": "envoy.filters.http.squash",
  "deprecated": "envoy.squash"
},

So I think some key/val structure is nicer to work with here, let's go with:

  envoyfilters:
    http_squash:
      canonical: "envoy.filters.http.squash"
      deprecated: "envoy.squash"
    listener_http_inspector:
      canonical: "envoy.filters.listener.http_inspector"
      deprecated: "envoy.listener.http_inspector"

Having converted your yaml to the GK input via kube-review, and adding the parameters to the input, I've put up this example on the playground: https://play.openpolicyagent.org/p/o8QrD3xj1y

It boils down to this:

package play

violation[{"msg": msg}] {
    config_patch := input.review.object.spec.configPatches[match][listener][filterchain][filter][_].name
    some depr_name # name of deprecation
    input.parameters.envoyfilters[depr_name].deprecated == config_patch
    better := input.parameters.envoyfilters[depr_name].canonical

    msg := sprintf("REVIEW OBJECT: %s, violation %s, use %s instead", [config_patch, depr_name, better])
}

Looping over the different config patches happens implicitly by Rego trying to satisfy

    config_patch := input.review.object.spec.configPatches[match][listener][filterchain][filter][_].name

and then we need to look up in our parameters if one of the to-be-applied patches is deprecated:

    some depr_name # name of deprecation
    input.parameters.envoyfilters[depr_name].deprecated == config_patch
    better := input.parameters.envoyfilters[depr_name].canonical
sr_
  • 547
  • 5
  • 14
1

When you use a looping construct like deprecated := input.parameters.envoyfilters[_].deprecated, it does not return a list, but assigns the deprecated variable to each item matching the expression. Your policy could thus be simplified to just:

violation[{"msg": msg}] {
    config_patch := input.review.object.spec.configPatches[match][listener][filterchain][filter][_].name
    deprecated := input.parameters.envoyfilters[_].deprecated
    
    config_patch == deprecated

    msg := sprintf("deprecated config patch: %v", [config_patch])
}

Full example on the Rego Playground.

Devoops
  • 2,018
  • 8
  • 21