TT Lab
Get started
Learn Learning paths Courses

Policy as Code

Writing Required-Label and Registry-Restriction Policies

Continue in TT Lab

Goal

Write for yourself a validating policy that requires mandatory labels and a deny rule that restricts the registry, and check the pass and fail decisions and the policy report locally. At the end, you express a legitimate exception within a narrow scope.

Why it matters

In a validating policy, what causes incidents is not the condition expression but the match scope. If the condition is wrong, testing catches it, but if the match is wrong, the policy quietly does nothing, so nobody notices. That is why trying both a resource that should pass and a resource that should be blocked is the basic skill of policy writing. Another point: the denial message is half of the policy. All a person whose deployment was blocked can see is the single line kubectl apply spat out, and if that line is unfriendly, the whole cost of operating the policy comes back as inquiries. Finally, this environment has no policy engine controller running, so the cluster does not actually block a bad Pod. You check the decisions locally with kyverno apply, and grading also looks at the structure of the policy YAML and its execution results.

Steps

  1. Create /root/policy/validate/require-labels.yaml. apiVersion: kyverno.io/v1, kind: ClusterPolicy, and metadata.name is require-labels. Set spec.validationFailureAction to Enforce (or set the rule's validate.failureAction to Enforce), set spec.background to true, and name spec.rules[0].name as check-required-labels.
  2. In the same rule, specify Pod with match.any[0].resources.kinds and pol-lab with namespaces. And in the rule, exclude kube-system with exclude.any[0].resources.namespaces.
  3. Under validate.pattern.metadata.labels, require the two labels app.kubernetes.io/name and team, each as "?*". Put a message in the same validate, at least 15 characters long, written so that it is clear what needs to be fixed.
  4. Run kyverno apply /root/policy/validate/require-labels.yaml --resource /opt/lab/fixtures/policy/resources/good-pod.yaml and save the result to /root/policy/validate/out/pass.txt. The result must show the policy name require-labels and a pass indicator, and the failure count must be 0.
  5. Run the same policy against /opt/lab/fixtures/policy/resources/bad-pod.yaml and save it to /root/policy/validate/out/fail.txt (failure count of at least 1, policy name included). And in /root/policy/validate/out/fail-note.txt, write in Korean which label was missing and caused the failure.
  6. In /root/policy/validate/restrict-registry.yaml, create a second ClusterPolicy. Use validate.deny.conditions.all (or any) in the rule, and put registry.labhub.io as the allowed registry so that images from anywhere else are denied. Then save the result of running it against /opt/lab/fixtures/policy/resources/bad-registry.yaml to /root/policy/validate/out/registry.txt (a denial/failure must be visible).
  7. Add preconditions to the rule in restrict-registry.yaml. Use a request context variable such as {{ request.operation }} so that the rule runs only for CREATE and UPDATE. And in /root/policy/validate/out/precondition-note.txt, write the difference between preconditions and deny — it must include both the point that if preconditions is false the rule is skipped, and the point that deny rejects as the result of the rule having run.
  8. Run require-labels.yaml with both good-pod and bad-pod passed as --resource and with --policy-report, and save it to /root/policy/validate/out/policy-report.yaml (summary.pass of at least 1, summary.fail of at least 1). And create a kind: PolicyException in /root/policy/validate/exception.yaml, narrowing spec.exceptions[0].policyName to require-labels, ruleNames to check-required-labels, and spec.match.any[0].resources.names by name, as in legacy-batch-*.

Notes

Set up the policy skeleton and enforcement mode

Create /root/policy/validate/require-labels.yaml. apiVersion: kyverno.io/v1, kind: ClusterPolicy, and metadata.name is require-labels. Set spec.validationFailureAction to Enforce (or set the rule's validate.failureAction to Enforce), set spec.background to true, and name spec.rules[0].name as check-required-labels.

ClusterPolicy is a CRD in the kyverno.io group. In spec you decide whether to block or only record on failure, and whether to treat existing resources as check targets as well.

Narrow the include and exclude targets

In the same rule, specify Pod with match.any[0].resources.kinds and pol-lab with namespaces. And in the rule, exclude kube-system with exclude.any[0].resources.namespaces.

In match, you write the kind and namespace as resources under any. If you leave out the paired exclude, system namespaces also become targets.

Write the required label pattern and denial message

Under validate.pattern.metadata.labels, require the two labels app.kubernetes.io/name and team, each as "?*". Put a message in the same validate, at least 15 characters long, written so that it is clear what needs to be fixed.

You write the pattern in the same shape as the object to be checked. There is a separate operator that means "it just has to exist." The message is the only document the denied person reads.

Run it against a Pod that should pass

Run kyverno apply /root/policy/validate/require-labels.yaml --resource /opt/lab/fixtures/policy/resources/good-pod.yaml and save the result to /root/policy/validate/out/pass.txt. The result must show the policy name require-labels and a pass indicator, and the failure count must be 0.

The kyverno CLI evaluates the policy file and the manifest received with --resource locally. Choose an output format in which the policy name shows in the result.

Run it against a violating Pod and write down the cause

Run the same policy against /opt/lab/fixtures/policy/resources/bad-pod.yaml and save it to /root/policy/validate/out/fail.txt (failure count of at least 1, policy name included). And in /root/policy/validate/out/fail-note.txt, write in Korean which label was missing and caused the failure.

If you check only passes, you cannot tell it apart from a policy that matches nothing. Also remember that if there is a failure, the exit code is 1.

Write a deny rule that restricts the registry

In /root/policy/validate/restrict-registry.yaml, create a second ClusterPolicy. Use validate.deny.conditions.all (or any) in the rule, and put registry.labhub.io as the allowed registry so that images from anywhere else are denied. Then save the result of running it against /opt/lab/fixtures/policy/resources/bad-registry.yaml to /root/policy/validate/out/registry.txt (a denial/failure must be visible).

A condition like "deny if it is not in the list" is hard to express with a pattern. Think about which of any and all under conditions is the right one.

Narrow the rule's scope with preconditions

Add preconditions to the rule in restrict-registry.yaml. Use a request context variable such as {{ request.operation }} so that the rule runs only for CREATE and UPDATE. And in /root/policy/validate/out/precondition-note.txt, write the difference between preconditions and deny — it must include both the point that if preconditions is false the rule is skipped, and the point that deny rejects as the result of the rule having run.

If preconditions is false, the rule is not even evaluated. You pass only if you write up in a file how it differs from deny.

Build a policy report and a narrow exception

Run require-labels.yaml with both good-pod and bad-pod passed as --resource and with --policy-report, and save it to /root/policy/validate/out/policy-report.yaml (summary.pass of at least 1, summary.fail of at least 1). And create a kind: PolicyException in /root/policy/validate/exception.yaml, narrowing spec.exceptions[0].policyName to require-labels, ruleNames to check-required-labels, and spec.match.any[0].resources.names by name, as in legacy-batch-*.

For one pass and one failure to be contained in a single report summary, you must feed in both resources. Narrow the exception down to the policy name, rule name, and resource name.