> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Lab Solution Add CEL validation to WebApp

> Adds a Kubernetes ValidatingAdmissionPolicy using CEL to block images with latest tag and enforce WebApp replicas between 1 and 10

Validation is the API-server gatekeeper for writes to the cluster. In this lesson we add two WebApp validation rules at that gate using a ValidatingAdmissionPolicy backed by CEL expressions:

* Deny images that use the floating `:latest` tag.
* Deny explicit replica counts outside the supported range (1–10). The `replicas` field is optional for the WebApp API, so omission remains allowed.

These checks are implemented in two pieces:

1. A `ValidatingAdmissionPolicy` that describes which resources to match and contains the CEL expressions.
2. A `ValidatingAdmissionPolicyBinding` that attaches the policy and selects the enforcement action. Here the enforcement action is `Deny`, so any failed expression blocks the write before the object is stored.

<Callout icon="lightbulb" color="#1CB2FE">
  A policy without a binding is like a rule written down but not posted at the door — it won't be enforced until it is bound.
</Callout>

## What this policy enforces

* Image rule: prevents images that explicitly end with `:latest`. The expression allows the `image` field to be omitted, or if present requires it not to end with `:latest`.
* Replicas rule: allows omitting `replicas`, or if present requires its value to be an integer between 1 and 10 inclusive.

Summary table

| Validation | Purpose                                         | CEL expression                                                                              |
| ---------: | ----------------------------------------------- | ------------------------------------------------------------------------------------------- |
|      Image | Deny images tagged `:latest`, allow omission    | `!has(object.spec.image) \|\| !object.spec.image.endsWith(':latest')`                       |
|   Replicas | Allow omission or require `1 <= replicas <= 10` | `!has(object.spec.replicas) \|\| (object.spec.replicas >= 1 && object.spec.replicas <= 10)` |

## ValidatingAdmissionPolicy

This resource matches `webapps.webapp.kodekloud.com/v1` on `CREATE` and `UPDATE` and contains the two CEL validations shown below:

```yaml theme={null}
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: webapp-validation
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: ["webapp.kodekloud.com"]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["webapps"]
  validations:
    - expression: "!has(object.spec.image) || !object.spec.image.endsWith(':latest')"
      message: "image must not use the :latest tag"
    - expression: "!has(object.spec.replicas) || (object.spec.replicas >= 1 && object.spec.replicas <= 10)"
      message: "replicas must be between 1 and 10"
```

Notes:

* `failurePolicy: Fail` means the admission request is rejected on policy evaluation errors.
* The CEL `has()` check lets the field be optional; only present values are evaluated against the constraint.

<Callout icon="warning" color="#FF6B6B">
  Ensure your cluster supports `ValidatingAdmissionPolicy` resources in `admissionregistration.k8s.io/v1` before applying these manifests. Cluster feature availability can vary by Kubernetes distribution and version.
</Callout>

## ValidatingAdmissionPolicyBinding

Bind the policy and set enforcement to `Deny` so failed validations block writes:

```yaml theme={null}
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: webapp-validation-binding
spec:
  policyName: webapp-validation
  enforcementAction: "Deny"
```

## Apply the policy and confirm

Apply the policy and binding, then verify both exist:

```bash theme={null}
$ kubectl apply -f webapp-policy.yaml
validatingadmissionpolicy.admissionregistration.k8s.io/webapp-validation created
validatingadmissionpolicybinding.admissionregistration.k8s.io/webapp-validation-binding created

$ kubectl get validatingadmissionpolicy webapp-validation
NAME                VALIDATIONS   PARAMKIND   AGE
webapp-validation   2             <unset>     20s

$ kubectl get validatingadmissionpolicybinding webapp-validation-binding
NAME                        POLICYNAME            PARAMREF   AGE
webapp-validation-binding   webapp-validation     <unset>    37s
```

These commands confirm both halves are in place and the binding is active.

## Test resources

Use the following example manifests to validate policy behavior.

1. Test: image with `:latest` should be rejected

```yaml theme={null}
# bad-latest.yaml
apiVersion: webapp.kodekloud.com/v1
kind: WebApp
metadata:
  name: bad-latest
  namespace: webapp-demo
spec:
  image: nginx:latest
  replicas: 2
```

```bash theme={null}
$ kubectl apply -f bad-latest.yaml
The webapps "bad-latest" is invalid: ValidatingAdmissionPolicy 'webapp-validation' with binding 'webapp-validation-binding' denied request: image must not use the :latest tag
```

The denial happens at the API server before any controller reconciles the object.

2. Test: replicas outside allowed range should be rejected

```yaml theme={null}
# bad-replicas.yaml
apiVersion: webapp.kodekloud.com/v1
kind: WebApp
metadata:
  name: bad-replicas
  namespace: webapp-demo
spec:
  image: nginx:1.27
  replicas: 50
```

```bash theme={null}
$ kubectl apply -f bad-replicas.yaml
The webapps "bad-replicas" is invalid: ValidatingAdmissionPolicy 'webapp-validation' with binding 'webapp-validation-binding' denied request: replicas must be between 1 and 10
```

3. Test: valid WebApp should be accepted

```yaml theme={null}
# shop.yaml
apiVersion: webapp.kodekloud.com/v1
kind: WebApp
metadata:
  name: shop
  namespace: webapp-demo
spec:
  image: nginx:1.27
  replicas: 3
```

```bash theme={null}
$ kubectl apply -f shop.yaml
webapp.webapp.kodekloud.com/shop created
```

This demonstrates the policy blocks unsafe cases while allowing valid objects.

## Links and references

* [Kubernetes Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/)
* [ValidatingAdmissionPolicy API reference (admissionregistration.k8s.io)](https://kubernetes.io/docs/reference/generated/kubernetes-api/latest/)
* [Common Expression Language (CEL) for Kubernetes](https://kubernetes.io/docs/reference/strategy/expressions/)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/kubernetes-operators/module/06ac03ac-518a-4bc6-b2f8-6ed63fcb26d5/lesson/c541f201-dbde-4b6d-8b13-df799e55b753" />
</CardGroup>
