cel-k8s

CEL for Kubernetes - Production-Ready Policy Generator

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cel-k8s" with this command: npx skills add tyrchen/claude-skills/tyrchen-claude-skills-cel-k8s

CEL for Kubernetes - Production-Ready Policy Generator

Generate solid, high-quality, production-ready CEL (Common Expression Language) code for Kubernetes admission control, CRD validation, and security policy enforcement.

When to Use This Skill

Use this skill when the user wants to:

  • Write ValidatingAdmissionPolicy resources with CEL expressions

  • Create CRD validation rules using x-kubernetes-validations

  • Enforce security policies (Pod Security Standards, image restrictions, etc.)

  • Validate resource configurations (labels, annotations, resource limits)

  • Build admission control without external webhooks

  • Migrate from OPA/Gatekeeper/Kyverno to native Kubernetes CEL

  • Debug or optimize existing CEL expressions

CEL Quick Reference

Core Operators

// Comparison == != < <= > >=

// Logical && || !

// Arithmetic

      • / %

// Membership in // Check if element exists in collection

// Ternary condition ? trueValue : falseValue

Essential Functions

// Field existence (CRITICAL - always check before accessing optional fields) has(object.spec.field)

// String functions size(string) // Length contains(string, substring) // Contains check startsWith(string, prefix) // Prefix check endsWith(string, suffix) // Suffix check matches(string, regex) // Regex match split(string, delimiter) // Split to list lower(string) // Lowercase upper(string) // Uppercase trim(string) // Remove whitespace

// Collection functions size(list) // List length all(list, var, condition) // All elements satisfy exists(list, var, condition) // Any element satisfies exists_one(list, var, condition) // Exactly one satisfies filter(list, var, condition) // Filter elements map(list, var, transformation) // Transform elements

// Kubernetes-specific quantity(string) // Parse K8s quantity (e.g., "2Gi", "500m") isQuantity(string) // Validate quantity format url(string) // Parse URL

Available Context Variables

In ValidatingAdmissionPolicy:

  • object

  • The incoming resource being validated

  • oldObject

  • The existing resource (UPDATE operations)

  • request

  • Admission request metadata (user, operation, namespace)

  • params

  • Parameters from ValidatingAdmissionPolicyBinding

  • namespaceObject

  • The namespace resource

In CRD Validation (x-kubernetes-validations):

  • self

  • The field being validated

  • oldSelf

  • Previous field value (UPDATE)

Instructions for Writing CEL Policies

Step 1: Understand the Requirement

Before writing any CEL:

  • What resource types need validation? (Deployments, Pods, Services, etc.)

  • What operations should trigger validation? (CREATE, UPDATE, DELETE)

  • What specific conditions must be enforced?

  • Should violations block the request or just audit?

Step 2: Design the Expression

Follow these principles:

  1. Always use has() for optional fields:

// CORRECT - Safe field access has(object.spec.template.spec.securityContext) && object.spec.template.spec.securityContext.runAsNonRoot == true

// WRONG - Will error if field doesn't exist object.spec.template.spec.securityContext.runAsNonRoot == true

  1. Handle null/missing values gracefully:

// Check for labels existence before accessing has(object.metadata.labels) && 'app' in object.metadata.labels && object.metadata.labels['app'] == 'myapp'

  1. Use short-circuit evaluation:

// Fast checks first, expensive operations last has(object.metadata.labels) && // Fast: field existence 'app' in object.metadata.labels && // Medium: map lookup object.metadata.labels['app'].matches('^[a-z]+$') // Slow: regex

  1. Prefer positive assertions:

// BETTER - Clear intent object.spec.replicas >= 1 && object.spec.replicas <= 10

// AVOID - Double negatives !(object.spec.replicas < 1 || object.spec.replicas > 10)

Step 3: Write the ValidatingAdmissionPolicy

Use this structure:

apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: "policy-name.example.com" spec: failurePolicy: Fail # or Ignore for non-critical policies matchConstraints: resourceRules: - apiGroups: ["apps"] apiVersions: ["v1"] operations: ["CREATE", "UPDATE"] resources: ["deployments"] validations:

  • expression: "CEL expression here" message: "Human-readable error message" messageExpression: "'Dynamic message with ' + object.metadata.name"

Step 4: Create the Binding

apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicyBinding metadata: name: "policy-binding" spec: policyName: "policy-name.example.com" validationActions: [Deny] # or [Audit] for testing matchResources: namespaceSelector: matchLabels: environment: production

Step 5: Test Before Deploying

Use dry-run mode:

kubectl apply --dry-run=server -f test-resource.yaml

Start with Audit mode:

validationActions: [Audit] # Log violations, don't block

Check events for violations:

kubectl get events --field-selector reason=PolicyAudit

Common Policy Patterns

Security Policies

Require non-root containers:

validations:

  • expression: | has(object.spec.template.spec.securityContext) && has(object.spec.template.spec.securityContext.runAsNonRoot) && object.spec.template.spec.securityContext.runAsNonRoot == true message: "Pods must run as non-root user"

Disallow privileged containers:

validations:

  • expression: | !has(object.spec.template.spec.containers) || !object.spec.template.spec.containers.exists(c, has(c.securityContext) && has(c.securityContext.privileged) && c.securityContext.privileged == true ) message: "Privileged containers are not allowed"

Drop all capabilities:

validations:

  • expression: | object.spec.template.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.capabilities) && has(c.securityContext.capabilities.drop) && c.securityContext.capabilities.drop.exists(cap, cap == 'ALL') ) message: "All containers must drop ALL capabilities"

Restrict to approved registries:

validations:

  • expression: | object.spec.template.spec.containers.all(c, c.image.startsWith('myregistry.io/') || c.image.startsWith('gcr.io/myproject/') ) message: "Container images must come from approved registries"

Disallow latest tag:

validations:

  • expression: | object.spec.template.spec.containers.all(c, c.image.contains(':') && !c.image.endsWith(':latest') ) message: "Container images must not use 'latest' tag"

Resource Validation

Require resource limits:

validations:

  • expression: | object.spec.template.spec.containers.all(c, has(c.resources) && has(c.resources.limits) && has(c.resources.limits.memory) && has(c.resources.limits.cpu) && has(c.resources.requests) && has(c.resources.requests.memory) && has(c.resources.requests.cpu) ) message: "All containers must define CPU and memory limits and requests"

Enforce resource quotas:

validations:

  • expression: | object.spec.template.spec.containers.all(c, !has(c.resources.requests.memory) || quantity(c.resources.requests.memory) <= quantity('2Gi') ) message: "Memory requests cannot exceed 2Gi per container"

Label and Annotation Validation

Require specific labels:

validations:

  • expression: | has(object.metadata.labels) && 'app' in object.metadata.labels && 'environment' in object.metadata.labels && 'team' in object.metadata.labels message: "Resources must have 'app', 'environment', and 'team' labels"

Validate label values:

validations:

  • expression: | !has(object.metadata.labels) || !('environment' in object.metadata.labels) || object.metadata.labels['environment'] in ['dev', 'staging', 'prod'] message: "environment label must be one of: dev, staging, prod"

Validate naming conventions:

validations:

  • expression: | object.metadata.name.matches('^[a-z][a-z0-9-]*[a-z0-9]$') && object.metadata.name.size() <= 63 message: "Resource name must be lowercase alphanumeric with hyphens, max 63 chars"

Network Policies

Disallow hostNetwork:

validations:

  • expression: | !has(object.spec.template.spec.hostNetwork) || object.spec.template.spec.hostNetwork == false message: "hostNetwork is not allowed"

Disallow hostPath volumes:

validations:

  • expression: | !has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(v, !has(v.hostPath)) message: "hostPath volumes are not allowed"

CRD Validation Rules

For CustomResourceDefinitions, use x-kubernetes-validations :

apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: myresources.example.com spec: group: example.com versions:

  • name: v1 schema: openAPIV3Schema: type: object properties: spec: type: object properties: replicas: type: integer minimum: 1 maximum: 100 x-kubernetes-validations: - rule: "self >= 1 && self <= 100" message: "Replicas must be between 1 and 100" schedule: type: string x-kubernetes-validations: - rule: "self.matches('^(\\d+|\\)(/\\d+)?(\\s+(\\d+|\\)(/\\d+)?){4}$')" message: "Must be a valid cron expression" x-kubernetes-validations: - rule: "has(self.replicas) || has(self.schedule)" message: "Either replicas or schedule must be specified"

Performance Best Practices

Set Schema Constraints

Help the cost estimator by bounding collections:

properties: containers: type: array maxItems: 20 # Bound array iterations labels: type: object maxProperties: 50 # Bound map operations name: type: string maxLength: 253 # Bound string operations

Avoid O(n^2) Patterns

BAD - O(n^2): Nested iteration over same collection

  • expression: | object.spec.containers.all(c1, object.spec.containers.all(c2, c1.name != c2.name || c1 == c2 ) )

GOOD - O(n): Use unique check

  • expression: | object.spec.containers.map(c, c.name).size() == object.spec.containers.size() message: "Container names must be unique"

Use Targeted Match Rules

Limit policy scope to reduce evaluations:

matchConstraints: resourceRules:

  • apiGroups: ["apps"] # Specific group apiVersions: ["v1"] # Specific version operations: ["CREATE"] # Only CREATE, not every operation resources: ["deployments"] # Specific resource namespaceSelector: # Target specific namespaces matchLabels: enforce-policies: "true"

Debugging CEL Expressions

Common Errors and Fixes

Error: "no such key"

// Problem: Accessing map key that doesn't exist object.metadata.labels['app']

// Fix: Check key existence has(object.metadata.labels) && 'app' in object.metadata.labels && object.metadata.labels['app']

Error: "type mismatch"

// Problem: Comparing wrong types object.spec.replicas == "5"

// Fix: Use correct type object.spec.replicas == 5

Error: "no such field"

// Problem: Accessing field on null object object.spec.securityContext.runAsNonRoot

// Fix: Check parent existence has(object.spec.securityContext) && object.spec.securityContext.runAsNonRoot == true

Testing Commands

Test with dry-run

kubectl apply --dry-run=server -f resource.yaml

Check policy status

kubectl get validatingadmissionpolicy kubectl describe validatingadmissionpolicy <name>

View audit events

kubectl get events --field-selector reason=PolicyAudit

Check type checking warnings

kubectl get validatingadmissionpolicy <name> -o yaml | grep -A 20 typeChecking

Output Format

When generating CEL policies, always provide:

  • Complete ValidatingAdmissionPolicy YAML

  • Corresponding ValidatingAdmissionPolicyBinding YAML

  • Test resources (both passing and failing examples)

  • Explanation of each validation rule

  • Deployment instructions

Reference Files

  • CEL Syntax Reference - Complete language reference

  • Common Patterns - Reusable validation patterns

  • Templates - Production-ready policy templates

Kubernetes Version Compatibility

  • CEL in ValidatingAdmissionPolicy: GA in Kubernetes 1.30+

  • CEL in CRD validation: GA in Kubernetes 1.29+

  • Alpha/Beta: Available in earlier versions with feature gates

Always verify target cluster version before generating policies.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

surrealdb

No summary provided by upstream source.

Repository SourceNeeds Review
General

chat-history

No summary provided by upstream source.

Repository SourceNeeds Review
General

ai-image

No summary provided by upstream source.

Repository SourceNeeds Review
General

design-guide

No summary provided by upstream source.

Repository SourceNeeds Review