Introduction
Update 2020-05-16: Gatekeeper superseeds OPA so there is a new post, that replaces this one
Update 2019-09-08: after finding a critical bug causing my cluster to hang and becoming unusable after a restart I did some investigation and testing and have updated the project on Github.
Open Policy Agent is an open-source, general-purpose policy engine that enables unified, context-aware policy enforcement across the entire stack. OPA provides greater flexibility and expressiveness than hard-coded service logic or ad-hoc domain-specific languages and comes with powerful tooling to help anyone get started.
In this post we will explore OPA with the purpose of implementing a policy that prevents from inadvertently deleting Kubernetes namespaces annotated with protected: "yes"
.
This policy is intended to avoid that system and application namespaces get deleted by mistake.
All examples as well as the installation of OPA are on github.
Install OPA
The installation of OPA is covered in this readme.
Create the policy
The policy we want to create will run as an admission controller when deleting a namespace, and check whether the namespaces has an annotation protected: "yes"
. If that’s the case the deletion will be rejected.
This policy can be found here.
package kubernetes.admission
import data.kubernetes.namespaces
import input.request.object.metadata.annotations as annotations
deny[msg] {
input.request.kind.kind = "Namespace"
input.request.operation = "DELETE"
missing_required_annotations[msg]
}
# Require no "protected" annotation or protected="no" to allow namespace deletion
missing_required_annotations[msg] {
annotation = namespaces[input.request.namespace].metadata.annotations["protected"]
not annotation = "no"
msg = "Namespaces annotated with protected=yes can not be deleted"
The policy will run on any DELETE
operation for objects of the kind Namespace
, and will check if there an annotation protected
:
- if there is no annotation or the annotation
protected: "no"
, the policy will accept the deletion - is there is an annotation different from “no”, e.g.
protected: "yes"
the policy will deny the operation
Test the policy
Deploy the policy into the opa
namespace as a configmap:
kubectl -n opa create configmap protected-namespaces --from-file=protected-namespaces.rego
Create two random namespace:
kubectl create ns test1
kubectl create ns test2
Annotate the second namespace:
kubectl annotate ns test1 protected=yes
Delete the first namespace:
kubectl delete ns test1
This should have worked:
namespace "test1" deleted
Now try to delete the second namespace:
kubectl delete ns test2
This should have failed:
Error from server (Namespaces annotated with protected=yes can not be deleted): admission webhook "validating-webhook.openpolicyagent.org" denied the request: Namespaces annotated with protected=yes can not be deleted
If now we overwrite the annotation and retry the deletion will be performed:
kubectl annotate ns test2 protected=no --overwrite
namespace "test2" annotated
kubectl delete ns test2
namespace "test2" deleted
Conclusion
OPA offers a flexible way to formalize rules in a declarative way using the rego
language. The language certainly comes with a steep learning curve but the model for declaring and deploying policies is very flexible.