Skip to content

Instantly share code, notes, and snippets.

@JonnieDoe
Created December 15, 2020 17:58
Show Gist options
  • Save JonnieDoe/873b2387fc8943c853cde25581dadac4 to your computer and use it in GitHub Desktop.
Save JonnieDoe/873b2387fc8943c853cde25581dadac4 to your computer and use it in GitHub Desktop.
Konstraint PoC
  • 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.

Generate Constraints

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

Generate documentation

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment