|
2 | 2 | layout: blog
|
3 | 3 | title: "Kubernetes 1.30: Validating Admission Policy Is Generally Available"
|
4 | 4 | slug: validating-admission-policy-ga
|
5 |
| -date: 2024-04-01 |
6 |
| -canonicalUrl: https://www.k8s.dev/blog/2024/04/01/validating-admission-policy/ |
| 5 | +date: 2024-04-24 |
7 | 6 | ---
|
8 | 7 |
|
9 | 8 | **Author:** Jiahui Feng (Google)
|
10 | 9 |
|
11 |
| -We are excited to announce that Validating Admission Policy has reached its General Availability |
| 10 | +On behalf of the Kubernetes project, I am excited to announce that ValidatingAdmissionPolicy has reached |
| 11 | +**general availability** |
12 | 12 | as part of Kubernetes 1.30 release. If you have not yet read about this new declarative alternative to
|
13 | 13 | validating admission webhooks, it may be interesting to read our
|
14 | 14 | [previous post](/blog/2022/12/20/validating-admission-policies-alpha/) about the new feature.
|
15 | 15 | If you have already heard about Validating Admission Policy and you are eager to try it out, there is no better time to do it now.
|
16 | 16 |
|
17 | 17 | Let's have a taste of Validating Admission Policy by replacing a simple webhook.
|
18 | 18 |
|
19 |
| -# The Webhook |
| 19 | +## Example admission webhook |
20 | 20 | First, let's take a look at an example of a simple webhook. Here is an excerpt from a webhook that
|
21 | 21 | enforce `runAsNonRoot`, `readOnlyRootFilesystem`, `allowPrivilegeEscalation`, and `privileged` to be set to the least permissive values.
|
22 | 22 |
|
@@ -47,10 +47,9 @@ func verifyDeployment(deploy *appsv1.Deployment) error {
|
47 | 47 | }
|
48 | 48 | ```
|
49 | 49 |
|
50 |
| -Check out [the doc](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#what-are-admission-webhooks) |
51 |
| -for a refresher on how admission webhooks work. Or, see the [full code](https://gist.github.com/jiahuif/2653f2ce41fe6a2e5739ea7cd76b182b) of this webhook to follow along this walkthrough. |
| 50 | +Check out [What are admission webhooks?](/docs/reference/access-authn-authz/extensible-admission-controllers/#what-are-admission-webhooks) Or, see the [full code](https://gist.github.com/jiahuif/2653f2ce41fe6a2e5739ea7cd76b182b) of this webhook to follow along this walkthrough. |
52 | 51 |
|
53 |
| -# The Policy |
| 52 | +## The policy |
54 | 53 | Now let's try to recreate the validation faithfully with a ValidatingAdmissionPolicy.
|
55 | 54 | ```yaml
|
56 | 55 | apiVersion: admissionregistration.k8s.io/v1
|
@@ -127,8 +126,9 @@ Next, let's create a namespace for our tests.
|
127 | 126 | ```shell
|
128 | 127 | kubectl create namespace policy-test
|
129 | 128 | ```
|
130 |
| -Then, bind the policy to the namespace. But at this point, we set the action to `Warn` |
131 |
| -so that the policy prints out warnings instead of rejecting the requests. |
| 129 | +Then, I bind the policy to the namespace. But at this point, I set the action to `Warn` |
| 130 | +so that the policy prints out [warnings](/blog/2020/09/03/warnings/) instead of rejecting the requests. |
| 131 | +This is especially useful to collect results from all expressions during development and automated testing. |
132 | 132 | ```yaml
|
133 | 133 | apiVersion: admissionregistration.k8s.io/v1
|
134 | 134 | kind: ValidatingAdmissionPolicyBinding
|
@@ -178,12 +178,12 @@ Error from server: error when creating "STDIN": admission webhook "webhook.examp
|
178 | 178 | Looks great! The policy and the webhook give equivalent results.
|
179 | 179 | After a few other cases, when we are confident with our policy, maybe it is time to do some cleanup.
|
180 | 180 |
|
181 |
| -- For every expression, we repeat access to `object.spec.template.spec.containers` and each `securityContext` in every expression; |
| 181 | +- For every expression, we repeat access to `object.spec.template.spec.containers` and to each `securityContext`; |
182 | 182 | - There is a pattern of checking presence of a field and then accessing it, which looks a bit verbose.
|
183 | 183 |
|
184 |
| -Fortunately, with the release of Kubernetes 1.28, we have new solutions for both issues. |
| 184 | +Fortunately, since Kubernetes 1.28, we have new solutions for both issues. |
185 | 185 | Variable Composition allows us to extract repeated sub-expressions into their own variables.
|
186 |
| -CEL library has also added support for [optionals](https://github.com/google/cel-spec/wiki/proposal-246), which are excellent to work with fields that are, well, optional. |
| 186 | +Kubernetes enables [the optional library](https://github.com/google/cel-spec/wiki/proposal-246) for CEL, which are excellent to work with fields that are, you guessed it, optional. |
187 | 187 |
|
188 | 188 | With both features in mind, let's refactor the policy a bit.
|
189 | 189 | ```yaml
|
@@ -213,4 +213,71 @@ spec:
|
213 | 213 | message: 'all containers must NOT set allowPrivilegeEscalation to true'
|
214 | 214 | - expression: variables.securityContexts.all(c, c.?privileged != optional.of(true))
|
215 | 215 | message: 'all containers must NOT set privileged to true'
|
216 |
| -``` |
| 216 | +``` |
| 217 | +The policy is now much cleaner and more readable. Update the policy, and you should see |
| 218 | +it function the same as before. |
| 219 | + |
| 220 | +Now let's change the policy binding from warning to actually denying requests that fail validation. |
| 221 | +```yaml |
| 222 | +apiVersion: admissionregistration.k8s.io/v1 |
| 223 | +kind: ValidatingAdmissionPolicyBinding |
| 224 | +metadata: |
| 225 | + name: "pod-security.policy-binding.example.com" |
| 226 | +spec: |
| 227 | + policyName: "pod-security.policy.example.com" |
| 228 | + validationActions: ["Deny"] |
| 229 | + matchResources: |
| 230 | + namespaceSelector: |
| 231 | + matchLabels: |
| 232 | + "kubernetes.io/metadata.name": "policy-test" |
| 233 | +``` |
| 234 | +And finally, remove the webhook. Now the result should include only messages from |
| 235 | +the policy. |
| 236 | +```shell |
| 237 | +kubectl create -n policy-test -f- <<EOF |
| 238 | +apiVersion: apps/v1 |
| 239 | +kind: Deployment |
| 240 | +metadata: |
| 241 | + labels: |
| 242 | + app: nginx |
| 243 | + name: nginx |
| 244 | +spec: |
| 245 | + selector: |
| 246 | + matchLabels: |
| 247 | + app: nginx |
| 248 | + template: |
| 249 | + metadata: |
| 250 | + labels: |
| 251 | + app: nginx |
| 252 | + spec: |
| 253 | + containers: |
| 254 | + - image: nginx |
| 255 | + name: nginx |
| 256 | + securityContext: |
| 257 | + privileged: true |
| 258 | + allowPrivilegeEscalation: true |
| 259 | +EOF |
| 260 | +``` |
| 261 | +```text |
| 262 | +The deployments "nginx" is invalid: : ValidatingAdmissionPolicy 'pod-security.policy.example.com' with binding 'pod-security.policy-binding.example.com' denied request: all containers must set runAsNonRoot to true |
| 263 | +``` |
| 264 | +Please notice that, by design, the policy will stop evaluation after the first expression that causes the request to be denied. |
| 265 | +This is different from what happens when the expressions generate only warnings. |
| 266 | + |
| 267 | +## Set up monitoring |
| 268 | +Unlike a webhook, a policy is not a dedicated process that can expose its own metrics. |
| 269 | +Instead, you can use metrics from the API server in their place. |
| 270 | + |
| 271 | +Here are some examples in Prometheus Query Language of common monitoring tasks. |
| 272 | + |
| 273 | +To find the 95th percentile execution duration of the policy shown above. |
| 274 | +```text |
| 275 | +histogram_quantile(0.95, sum(rate(apiserver_validating_admission_policy_check_duration_seconds_bucket{policy="pod-security.policy.example.com"}[5m])) by (le)) |
| 276 | +``` |
| 277 | + |
| 278 | +To find the rate of the policy evaluation. |
| 279 | +```text |
| 280 | +rate(apiserver_validating_admission_policy_check_total{policy="pod-security.policy.example.com"}[5m]) |
| 281 | +``` |
| 282 | + |
| 283 | +You can read [the metrics reference](/docs/reference/instrumentation/metrics/) to learn more about the metrics above. |
0 commit comments