Using Gatekeeper’s External Data Provider for validation with an Image Registry API.
I recently ran across the Gatekeeper External Data Provider Feature and realized it could solve a problem I have wanted to try solve for a while. The problem was Red Hat’s image registry provides very rich metadata information like Deprecation Status, Image Lifecycle e.t.c about it’s images, and I had wanted to use that metadata information to validate image’s Kubernetes Deployments. This article covers my experience writing my own custom provider and deploying it on OpenShift to achieve that purpose.
Let’s start with what Gatekeeper is — Gatekeeper is a policy controller allowing for the enforcement of policies on Kubernetes. Gatekeeper policies are written in rego which is the policy language for the Open Policy Agent Framework. Gatekeeper provides rich validation/mutation capabilities on-cluster, but what happens when the data we need is external to our cluster — We use the External Data Feature. You’re all caught up!
In my case the data I want to use for validation is behind an API as mentioned which is how I started researching the external data feature. The external data feature uses Providers which is the component that holds the logic for connecting to external data sources to help extend Gatekeeper. So in my case, what I was hoping to achieve is diagrammed below.
I couldn't find an already written provider that provided that functionality so I had to write mine. Providers are written in Go, which isn't my strongest language but Gatekeeper provides very good documentation and a dummy provider, which made it easier to reverse engineer my own Provider.
Most of the provider code could be broken into the following functions:
// Code Structure Sample - Full Code Link below - provider.go
func main() {
//start a listening https server
}
func validate(w http.ResponseWriter, req *http.Request) {
// Recieve a http request from Gatkeeper Controller at /validate path
// Parse image details from Gatkeeper Request
// Call API via callAPI
// Send Gatekeeper Controller Response Information
}
func callAPI(){
// Make Request to External Data API
}
We can now create a rego template and constraint on the OpenShift Cluster to describe how we want to validation to work. Rego template described below:
package k8sexternaldata
images := [img | img = input.review.object.spec.template.spec.containers[_].image]
# send external data request
response := external_data({"provider": "rh-registry-gatekeeper-provider-validate", "keys": images})
violation[{"msg": msg}] {
#Check For Deprecation
not response_unsupported_registry(response)
some i
imgresponse = response.responses[i]
imgresponse[j][k].key == "Deprecated"
imgresponse[j][k].value == "true"
imgname := imgresponse[0]
msg := sprintf("Image %s is Deprecated", [imgname])
}
violation[{"msg": msg}] {
#Check For Image EOL
not response_unsupported_registry(response)
some i
imgresponse = response.responses[i]
imgresponse[j][k].key == "EOLDate"
imgresponse[j][k].value == "true"
"EOLDateValue" == imgresponse[j][m].key
imgname := imgresponse[0]
eoldatevalue := imgresponse[j][m].value
msg := sprintf("Image %s went EOL on %s", [imgname, eoldatevalue])
}
violation[{"msg": msg}] {
#Check For Image Health Grade
not response_unsupported_registry(response)
some i
imgresponse = response.responses[i]
imgresponse[j][k].key == "HealthGrade"
imgresponse[j][k].value == "true"
"HealthGradeValue" == imgresponse[j][m].key
imgname := imgresponse[0]
healthgradevalue := imgresponse[j][m].value
msg := sprintf("Image %s does not have a good health grade, Health Grade is %s", [imgname, healthgradevalue])
}
#Not adding violation based on error response to allow for fail-open
With our provider created we can deploy it as a Kubernetes deployment where it listens to requests from the gatekeeper controller ready to Provide validation services. A plus for deploying this feature on OpenShift is the OpenShift Service CA which helps generate an automatic cert, so we can have mTLS between our provider and Gatekeeper.
So what does the experience look like for a user. If a user was to deploy an image that was deprecated they would get a message describing the reason the image was not allowed for creation. See samples
Gatekeeper provides really valuable capabilities to allow validation based on data that might be in sources external to the cluster. This allows organizations to deliver on their governance and security goals. And I hope you found this article was a useful example for how that can be achieved.
Thanks!
Github Link: