From tchen-skills
Writes production-ready CEL expressions for Kubernetes ValidatingAdmissionPolicies, CRD validation rules, and security constraints. Useful for building admission control or migrating from OPA/Gatekeeper.
How this skill is triggered — by the user, by Claude, or both
Slash command
/tchen-skills:celThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate solid, high-quality, production-ready CEL (Common Expression Language) code for Kubernetes admission control, CRD validation, and security policy enforcement.
Generate solid, high-quality, production-ready CEL (Common Expression Language) code for Kubernetes admission control, CRD validation, and security policy enforcement.
Use this skill when the user wants to:
// Comparison
== != < <= > >=
// Logical
&& || !
// Arithmetic
+ - * / %
// Membership
in // Check if element exists in collection
// Ternary
condition ? trueValue : falseValue
// 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
In ValidatingAdmissionPolicy:
object - The incoming resource being validatedoldObject - The existing resource (UPDATE operations)request - Admission request metadata (user, operation, namespace)params - Parameters from ValidatingAdmissionPolicyBindingnamespaceObject - The namespace resourceIn CRD Validation (x-kubernetes-validations):
self - The field being validatedoldSelf - Previous field value (UPDATE)Before writing any CEL:
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
2. 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'
3. 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
4. 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)
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"
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
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
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"
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"
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"
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"
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"
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
# 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"
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"
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
# 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
When generating CEL policies, always provide:
Always verify target cluster version before generating policies.
npx claudepluginhub tyrchen/claude-skillsImplements policy-as-code enforcement using OPA Gatekeeper or Kyverno to validate and mutate Kubernetes resources. Use for enforcing resource standards, preventing security misconfigurations, and CI/CD shift-left validation.
Provides production-grade Kubernetes YAML patterns, kubectl debugging commands, and best practices for deployments, probes, RBAC, autoscaling, ConfigMaps/Secrets, and security.
Implements Open Policy Agent (OPA) and Gatekeeper for policy-as-code enforcement in Kubernetes and CI/CD pipelines, including Rego policy writing and admission control.