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
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