Writing Required-Label and Registry-Restriction Policies
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
- Create
/root/policy/validate/require-labels.yaml.apiVersion: kyverno.io/v1,kind: ClusterPolicy, andmetadata.nameisrequire-labels. Setspec.validationFailureActiontoEnforce(or set the rule'svalidate.failureActiontoEnforce), setspec.backgroundtotrue, and namespec.rules[0].nameascheck-required-labels. - In the same rule, specify
Podwithmatch.any[0].resources.kindsandpol-labwithnamespaces. And in the rule, excludekube-systemwithexclude.any[0].resources.namespaces. - Under
validate.pattern.metadata.labels, require the two labelsapp.kubernetes.io/nameandteam, each as"?*". Put amessagein the samevalidate, at least 15 characters long, written so that it is clear what needs to be fixed. - Run
kyverno apply /root/policy/validate/require-labels.yaml --resource /opt/lab/fixtures/policy/resources/good-pod.yamland save the result to/root/policy/validate/out/pass.txt. The result must show the policy namerequire-labelsand a pass indicator, and the failure count must be 0. - Run the same policy against
/opt/lab/fixtures/policy/resources/bad-pod.yamland 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. - In
/root/policy/validate/restrict-registry.yaml, create a secondClusterPolicy. Usevalidate.deny.conditions.all(orany) in the rule, and putregistry.labhub.ioas 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.yamlto/root/policy/validate/out/registry.txt(a denial/failure must be visible). - Add
preconditionsto the rule inrestrict-registry.yaml. Use a request context variable such as{{ request.operation }}so that the rule runs only forCREATEandUPDATE. And in/root/policy/validate/out/precondition-note.txt, write the difference between preconditions anddeny— it must include both the point that if preconditions is false the rule is skipped, and the point thatdenyrejects as the result of the rule having run. - Run
require-labels.yamlwith both good-pod and bad-pod passed as--resourceand with--policy-report, and save it to/root/policy/validate/out/policy-report.yaml(summary.passof at least 1,summary.failof at least 1). And create akind: PolicyExceptionin/root/policy/validate/exception.yaml, narrowingspec.exceptions[0].policyNametorequire-labels,ruleNamestocheck-required-labels, andspec.match.any[0].resources.namesby name, as inlegacy-batch-*.
Notes
- The basic form of the command is
kyverno apply <정책> --resource <매니페스트>(policy, then manifest).-tprints a table,--detailed-resultsprints details, and--policy-reportprints in report form. - If the policy name does not show in the result file, add
-tor--detailed-results. If you save only the summary line, the policy name is missing. - If there is even one failure, the exit code of
kyverno applyis 1. If it is not in a pipeline, you can leave it as it is. - If progress messages are mixed in before the
--policy-reportoutput,yqcannot read it. Cut from theapiVersion:line and save (sed -n '/^apiVersion:/,$p'). - Common mistake 1: leaving out
exclude. If system namespaces also become targets, the cluster is at risk. - Common mistake 2: applying an exception to the whole policy. An exception not narrowed by
ruleNamesand resourcenamesis the same as turning the policy off.
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.