-
Create the directory structure
mkdir -p policies/k8s-valid-pod-disruption-budget
-
Create the core
lib
module (if needed)mkdir -p policies/lib touch policies/lib/core.rego
-
Create the desired Rego policy
touch policies/k8s-valid-pod-disruption-budget/src.rego
# @title Only permit valid PodDisruptionBudgets # # This policy only permits the creation or update of a valid PDB. # What is an invalid PDB? # - PDBs with minAvailable = "100%" # - PDBs with maxUnavailable = 0 # - PDB with maxUnavailable = "0%" # # @enforcement dryrun # @kinds policy/PodDisruptionBudget package k8s_valid_pod_disruption_budget import data.lib.core isPdb { input.review.kind.kind == "PodDisruptionBudget" } # The object operations we are searching for. lookUpObjectOperations := {"CREATE", "UPDATE"} #----------------------------------------------------------- # Deny creation/update of PDBs with minAvailable = "100%" #----------------------------------------------------------- violation[{"msg": msg}] { # Check that the requested resource matches the desired requirements. isPdb # Type of request operation to match. input.review.operation == lookUpObjectOperations[_] input.review.object.spec.minAvailable == "100%" # If True => a violation occurred. msg := sprintf( "\nCannot create a PDB with minAvailable = 100%\nOPERATION: %s", [input.review.operation] ) } #----------------------------------------------------------- # Deny creation/update of PDBs with maxUnavailable = 0 #----------------------------------------------------------- violation[{"msg": msg}] { # Check that the requested resource matches the desired requirements. isPdb # Type of request operation to match. input.review.operation == lookUpObjectOperations[_] input.review.object.spec.maxUnavailable == 0 # If True => a violation occurred. msg := sprintf( "\nCannot create a PDB with maxUnavailable = 0\nOPERATION: %s", [input.review.operation] ) } #----------------------------------------------------------- # Deny creation/update of PDBs with maxUnavailable = "0%" #----------------------------------------------------------- violation[{"msg": msg}] { # Check that the requested resource matches the desired requirements. isPdb # Type of request operation to match. input.review.operation == lookUpObjectOperations[_] input.review.object.spec.maxUnavailable == "0%" # If True => a violation occurred. msg := sprintf( "\nCannot create a PDB with maxUnavailable = 0%\nOPERATION: %s", [input.review.operation] ) }
-
Create the Rego unit test file
touch policies/k8s-valid-pod-disruption-budget/src_test.rego
package k8s_valid_pod_disruption_budget default response = {"allowed": true} main = { "apiVersion": "admission.k8s.io/v1beta1", "kind": "AdmissionReview", "response": response, } request_default = { "kind": "AdmissionReview", "apiVersion": "admission.k8s.io/v1beta1", "request": {""}, } request_create_pdb_with_100_percent_minAvailable = { "kind": "AdmissionReview", "apiVersion": "admission.k8s.io/v1beta1", "request": { "uid": "836c0cc6-096c-11e9-9a47-080027f60d22", "kind": { "group": "policy", "version": "v1beta1", "kind": "PodDisruptionBudget", }, "resource": { "group": "policy", "version": "v1beta1", "resource": "poddisruptionbudgets", }, "name": "pdb-example", "operation": "CREATE", "userInfo": { "username": "cluster-user", "groups": [ "system:masters", "system:authenticated", ], }, "object": { "apiVersion": "policy/v1beta1", "kind": "PodDisruptionBudget", "metadata": { "name": "pdb-example", "namespace": "default", }, "spec": { "minAvailable": "100%", "selector": {"matchLabels": {"app": "example"}}, }, }, "dryRun": false, }, } request_create_pdb_with_0_maxUnavailable = { "kind": "AdmissionReview", "apiVersion": "admission.k8s.io/v1beta1", "request": { "uid": "836c0cc6-096c-11e9-9a47-080027f60d22", "kind": { "group": "policy", "version": "v1beta1", "kind": "PodDisruptionBudget", }, "resource": { "group": "policy", "version": "v1beta1", "resource": "poddisruptionbudgets", }, "name": "pdb-example", "operation": "CREATE", "userInfo": { "username": "cluster-user", "groups": [ "system:masters", "system:authenticated", ], }, "object": { "apiVersion": "policy/v1beta1", "kind": "PodDisruptionBudget", "metadata": { "name": "pdb-example", "namespace": "default", }, "spec": { "maxUnavailable": 0, "selector": {"matchLabels": {"app": "example"}}, }, }, "dryRun": false, }, } request_create_pdb_with_0_percent_maxUnavailable = { "kind": "AdmissionReview", "apiVersion": "admission.k8s.io/v1beta1", "request": { "uid": "836c0cc6-096c-11e9-9a47-080027f60d22", "kind": { "group": "policy", "version": "v1beta1", "kind": "PodDisruptionBudget", }, "resource": { "group": "policy", "version": "v1beta1", "resource": "poddisruptionbudgets", }, "name": "pdb-example", "operation": "CREATE", "userInfo": { "username": "cluster-user", "groups": [ "system:masters", "system:authenticated", ], }, "object": { "apiVersion": "policy/v1beta1", "kind": "PodDisruptionBudget", "metadata": { "name": "pdb-example", "namespace": "default", }, "spec": { "maxUnavailable": "0%", "selector": {"matchLabels": {"app": "example"}}, }, }, "dryRun": false, }, } #----------------------------------------------------------- # Test: Creating a PDB with minAvailable = 100% is not # allowed #----------------------------------------------------------- test_create_pdb_with_100_percent_minAvailable_deny { res := main with input as request_create_pdb_with_100_percent_minAvailable trace(sprintf("[test_create_pdb_with_100_percent_minAvailable_deny] res = '%s'", [res])) res.response.allowed = false } #----------------------------------------------------------- # Test: Creating a PDB with maxUnavailable = 0 is not # allowed #----------------------------------------------------------- test_create_pdb_with_0_maxUnavailable_deny { res := main with input as request_create_pdb_with_0_maxUnavailable trace(sprintf("[test_create_pdb_with_0_maxUnavailable_deny] res = '%s'", [res])) res.response.allowed = false } #----------------------------------------------------------- # Test: Creating a PDB with maxUnavailable = 0% is not # allowed #----------------------------------------------------------- test_create_pdb_with_0_percent_maxUnavailable_deny { res := main with input as request_create_pdb_with_0_percent_maxUnavailable trace(sprintf("[test_create_pdb_with_0_percent_maxUnavailable_deny] res = '%s'", [res])) res.response.allowed = false }
-
Test the policy
opa test policies -v
NOTE
We can also use Conftest and declarative YAML unit test files instead of Rego.
When using konstraint create
, Konstraint will only generate templates and
constraints for policy files with at least one violation[]
rule.
konstraint create
For naming convention, please read: https://github.com/plexsystems/konstraint/blob/main/docs/constraint_creation.md#resource-naming
When using konstraint doc
, Konstraint will create documentation for each
policy file and assign a severity based on the rule names found in the policy.
konstraint doc
It will generate the policies.md
in the current directory.