Kyverno a OPA Gatekeeper — admission control a policy-as-code
Policy-as-code v Kubernetes je spôsob, ako deklaratívne presadzovať organizačné pravidlá: "každý pod musí mať resource limits", "image môže pochádzať iba z nášho registry", "HostPath volumes sú zakázané". Namiesto pravidiel v runbook-e alebo code review checklist-e, admission controller blokuje nevyhovujúce manifesty pri kubectl apply.
Kyverno (Nirmata, CNCF Graduated 2024) a OPA Gatekeeper (Styra + CNCF — OPA graduovalo 2021, Gatekeeper ako subprojekt) sú dva dominantné open-source nástroje v tejto oblasti. Obidva fungujú ako ValidatingAdmissionWebhook (a voliteľne MutatingAdmissionWebhook), ale rozchádzajú sa v filozofii, jazyku policy a operačnom modeli.
Tento článok ich porovnáva, ukazuje reálne policy pre typické K8s scenáre a pomôže rozhodnúť, ktorý nástroj patrí do vášho stacku.
Admission control v skratke
Každý kubectl apply prechádza cez niekoľko fáz API servera:
kubectl apply
│
▼
┌─────────────────────────────────────────────────────┐
│ kube-apiserver │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │Authentication│ → │ Authorization │ → │Admission │ │
│ │(kto je kto) │ │(čo smie robiť)│ │Controllers│ │
│ └─────────────┘ └──────────────┘ └────┬─────┘ │
│ │ │
│ Mutating webhooks ◄────┤ │
│ │ │
│ Validating webhooks ◄──┤ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ etcd │ │
│ └──────────┘ │
└─────────────────────────────────────────────────────┘
Mutating webhooks môžu upraviť manifest pred uložením (napr. automaticky pridať label, injektovať sidecar). Validating webhooks môžu len schváliť alebo zamietnuť — nemôžu meniť.
Oba nástroje používajú tieto webhooks. Kyverno + Gatekeeper sa inštalujú ako K8s deployments, ktoré sa registrujú do API servera ako webhook handlery.
Dve filozofie — YAML-native vs Rego
Najvýraznejší rozdiel medzi Kyverno a Gatekeeper je jazyk pre policy:
Kyverno — policy je natívne Kubernetes CRD s YAML syntaxou, ktorá je blízka K8s manifestom. Nemusíte sa učiť nový jazyk.
Gatekeeper — policy sa píšu v Rego, deklaratívnom jazyku OPA. Rego je silnejší (Turing-complete? formálne áno, ale obmedzený), ale má strmú učenlivú krivku.
Porovnanie rovnakej policy — "pod musí mať resource limits":
Kyverno policy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
spec:
validationFailureAction: Enforce
background: true
rules:
- name: check-resources
match:
any:
- resources:
kinds:
- Pod
validate:
message: "CPU a memory limits musia byť definované."
pattern:
spec:
containers:
- name: "*"
resources:
limits:
memory: "?*"
cpu: "?*"
Sedem riadkov validate pattern-u. "?*" znamená "nejaká hodnota" (anchor modifier).
Gatekeeper policy
Gatekeeper potrebuje dve komponenty — ConstraintTemplate (Rego logika) a Constraint (inštancia s parametrami):
# 1. ConstraintTemplate — definuje validátor v Rego
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequireresourcelimits
spec:
crd:
spec:
names:
kind: K8sRequireResourceLimits
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequireresourcelimits
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.limits.cpu
msg := sprintf("container '%s' nemá CPU limit", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.limits.memory
msg := sprintf("container '%s' nemá memory limit", [container.name])
}
# 2. Constraint — aplikuje ConstraintTemplate na konkrétne resources
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequireResourceLimits
metadata:
name: require-resource-limits
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- production
- staging
Ďalej použiteľnejšie keď máte viacero podobných kontrol — Rego je reusable. Pre jeden-off validation je Kyverno stručnejší.
Kyverno — deep dive
Architektúra
┌───────────────────────────────────────────────────┐
│ kyverno namespace │
│ │
│ ┌─────────────────┐ ┌────────────────────────┐ │
│ │ admission │ │ background controller │ │
│ │ controller │ │ (policy reports, │ │
│ │ (webhook) │ │ mutate existing) │ │
│ └────────┬────────┘ └────────────────────────┘ │
│ │ │
│ ┌────────▼────────┐ ┌────────────────────────┐ │
│ │ reports │ │ cleanup controller │ │
│ │ controller │ │ (scheduled deletion) │ │
│ └──────────────────┘ └────────────────────────┘ │
└───────────────────────────────────────────────────┘
Kyverno v1.10+ má multiple controller architecture (bolo v jednom pode, teraz rozdelené):
admission-controller— vyhodnocuje admission requestsbackground-controller— generuje resources, mutate existujúcereports-controller—PolicyReportCRDs pre auditcleanup-controller— scheduled deletion policies
Inštalácia
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno \
--namespace kyverno \
--create-namespace \
--set admissionController.replicas=3 \
--set backgroundController.replicas=2 \
--set reportsController.replicas=2 \
--set cleanupController.replicas=2 \
--version 3.3.0
# Komunitné policy (ClusterPolicies pre typické scenáre)
helm install kyverno-policies kyverno/kyverno-policies \
--namespace kyverno \
--set podSecurityStandard=restricted
Produkčný setup: minimálne 3 admission replicas pre HA (high availability cruciálne — ak Kyverno padne, admission môže blokovať celé klaster deploy-y, alebo propagovať failure policy).
Kyverno operations — validate, mutate, generate, verifyImages, cleanup
Kyverno podporuje 5 rôznych typov pravidiel:
- validate — schváliť alebo zamietnuť manifest
- mutate — upraviť manifest pred uložením (pridať labels, defaulty)
- generate — vytvoriť súvisiace resources (napr. pri vytvorení Namespace automaticky pridať NetworkPolicy)
- verifyImages — Cosign signature verification (supply chain security)
- cleanup — TTL-based delete existujúcich resources
Príklad každého:
# Mutate: automaticky pridať label s cost-center
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-cost-center-label
spec:
rules:
- name: add-label
match:
any:
- resources:
kinds: [Pod, Deployment]
mutate:
patchStrategicMerge:
metadata:
labels:
cost-center: "{{ request.namespace.metadata.annotations.\"cost-center\" || 'unknown' }}"
# Generate: každý nový Namespace dostane default NetworkPolicy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-default-netpol
spec:
rules:
- name: default-deny
match:
any:
- resources:
kinds: [Namespace]
generate:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
name: default-deny
namespace: "{{ request.object.metadata.name }}"
data:
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
# VerifyImages: iba signed imagy z nášho registry
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-signed-images
spec:
validationFailureAction: Enforce
webhookTimeoutSeconds: 30
rules:
- name: check-signature
match:
any:
- resources:
kinds: [Pod]
verifyImages:
- imageReferences:
- "registry.example.sk/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/example-sk/*/.github/workflows/release.yml@refs/heads/main"
issuer: "https://token.actions.githubusercontent.com"
# Cleanup: každá dev pod staršia ako 7 dní zmizne
apiVersion: kyverno.io/v2beta1
kind: ClusterCleanupPolicy
metadata:
name: cleanup-old-dev-pods
spec:
schedule: "0 2 * * *" # každú noc 2 AM
match:
any:
- resources:
kinds: [Pod]
namespaces: [dev]
conditions:
all:
- key: "{{ target.metadata.creationTimestamp | time_to_hours }}"
operator: GreaterThan
value: 168 # 7 × 24
OPA Gatekeeper — deep dive
Architektúra
┌─────────────────────────────────────────────────┐
│ gatekeeper-system namespace │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ gatekeeper- │ │ gatekeeper- │ │
│ │ controller- │ │ audit │ │
│ │ manager (webhook) │ │ (existing res │ │
│ │ │ │ audit cron) │ │
│ └───────────────────┘ └───────────────────┘ │
│ │
│ OPA Rego runtime (embedded in controller) │
└─────────────────────────────────────────────────┘
Gatekeeper má jednoduchú architektúru — jeden controller s embedded OPA runtime. Menej komponent, ale všetko beží v jednom pode.
Inštalácia
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm install gatekeeper gatekeeper/gatekeeper \
--namespace gatekeeper-system \
--create-namespace \
--set replicas=3 \
--version 3.17.0
# Gatekeeper Library (preddefinované policy)
git clone https://github.com/open-policy-agent/gatekeeper-library
kubectl apply -f gatekeeper-library/library/general/
Rego — primer
Rego je deklaratívny jazyk, syntakticky medzi Datalog a Prolog. Hlavné koncepty:
package k8spsp # packaging
# Boolean rule: fires when matched
deny[msg] {
input.review.object.spec.hostNetwork == true
msg := "hostNetwork is not allowed"
}
# Set comprehension (multiple violations)
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
# ilustratívne — v reálnom policy kontrolujte runAsUser == 0 alebo runAsNonRoot == false
container.securityContext.runAsNonRoot == false
msg := sprintf("container %s môže bežať ako root", [container.name])
}
# Helper function
is_privileged(container) = true {
container.securityContext.privileged == true
}
# Iterate collections
violation[{"msg": msg}] {
some i
container := input.review.object.spec.containers[i]
is_privileged(container)
msg := sprintf("container[%d] je privileged", [i])
}
Pre ConstraintTemplate sa Rego balí do spec.targets[].rego.
Constraint Templates + Constraints
Tento dvojdielny prístup je zároveň sila aj slabosť Gatekeeper:
- Sila:
ConstraintTemplateje znovu použiteľná definícia (napr. "require label X"). Pre konkrétny use-case vytvoríteConstraints parametrami. - Slabosť: dvojakorá komplexita pre jednoduché pravidlá, ktoré by v Kyverne boli jeden YAML.
Príklad: require label pomocou ConstraintTemplate:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("you must provide labels: %v", [missing])
}
Constraint:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: namespace-must-have-owner
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels:
- owner
- team
- cost-center
Teraz môžete rovnakú K8sRequiredLabels použiť opakovane s rôznymi parametrami.
Validation vs Mutation v Gatekeeper
Gatekeeper historicky robil iba validation. Mutation podpora pribudla v 3.8 a používa iný CRD typ:
apiVersion: mutations.gatekeeper.sh/v1
kind: Assign
metadata:
name: always-pull-images
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
scope: Namespaced
location: "spec.containers[name:*].imagePullPolicy"
parameters:
assign:
value: "Always"
Gatekeeper mutation je stále menej používaná ako validation — väčšina tímov mutation rieši cez Kyverno alebo externé tools.
Porovnanie Kyverno vs Gatekeeper
| Kritérium | Kyverno | OPA Gatekeeper |
|---|---|---|
| Policy jazyk | YAML (K8s-native) | Rego (deklaratívny) |
| Learning curve | ✅ Plochá pre K8s ľudí | ❌ Strmá (Rego) |
| Validation | ✅ | ✅ |
| Mutation | ✅ Prvotriedny use-case | ⚠️ Pozdejší add-on |
| Generate resources | ✅ Unikátne | ❌ |
| Image verification (Cosign) | ✅ Prvotriedna | ⚠️ Vyžaduje externý tool |
| Cleanup policies | ✅ | ❌ |
| Pre-existing policies (library) | ✅ Kyverno Policies (~50 batch) | ✅ Gatekeeper Library |
| PolicyReport CRDs | ✅ | ⚠️ Cez Audit |
| Background scanning | ✅ Scans existing resources | ✅ Audit cron |
| Performance | ✅ O(n) — paralelný eval | ⚠️ Rego compile overhead per policy |
| Multi-cluster policies | ✅ Cez ArgoCD/Flux | ✅ |
| CNCF status | Graduated (2024) | Graduated (OPA, 2021) |
| Governance | Nirmata | Styra + CNCF |
| Ecosystem outside K8s | K8s-only | ✅ OPA je univerzálny (API, Terraform, CI/CD) |
| Community size | ✅ Rastúca (K8s-native appeal) | ✅ Staršia, etablovaná |
Praktické policy pre typické scenáre
1. Pod Security Standards — restricted level
Kyverno má pred-balenú kompletnú sadu:
helm install kyverno-policies kyverno/kyverno-policies \
--namespace kyverno \
--set podSecurityStandard=restricted
To aplikuje ~15 policies (no privileged, no hostPath, runAsNonRoot, atď.).
2. Iba schválené registries
# Kyverno
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: allowed-registries
spec:
validationFailureAction: Enforce
rules:
- name: check-image
match:
any:
- resources: {kinds: [Pod]}
validate:
message: "Image musí pochádzať z povoleného registry."
pattern:
spec:
containers:
- image: "registry.example.sk/* | ghcr.io/example-sk/*"
3. Externé služby nesmú ísť na internet
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: no-external-networking
spec:
validationFailureAction: Enforce
rules:
- name: block-external-egress
match:
any:
- resources: {kinds: [NetworkPolicy]}
validate:
message: "NetworkPolicy nesmie povoľovať egress na 0.0.0.0/0."
deny:
conditions:
any:
- key: "{{ request.object.spec.egress[].to[].ipBlock.cidr }}"
operator: AnyIn
value: ["0.0.0.0/0"]
4. Mandatory labels
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-team-label
spec:
validationFailureAction: Enforce
rules:
- name: check-team
match:
any:
- resources: {kinds: [Deployment, StatefulSet]}
validate:
message: "Deployment musí mať 'team' label."
pattern:
metadata:
labels:
team: "?*"
Pre-production testing — kyverno test a gator
Obe platformy podporujú CLI testing policies na mock resources pred aplikáciou do klastera.
Kyverno test
# Adresárová štruktúra
policies/
├── require-resources.yaml
└── test/
├── kyverno-test.yaml
├── good-pod.yaml
└── bad-pod.yaml
# kyverno-test.yaml
name: require-resources-test
policies:
- ../require-resources.yaml
resources:
- good-pod.yaml
- bad-pod.yaml
results:
- policy: require-resource-limits
rule: check-resources
resource: good-pod
result: pass
- policy: require-resource-limits
rule: check-resources
resource: bad-pod
result: fail
# Spustenie
kyverno test policies/test/
OPA Gatekeeper — gator CLI
# Test ConstraintTemplate + Constraint + input
gator test -f templates/ -f constraints/ -f inputs/
Obe CLIs v GitHub Actions = reliable shift-left — policy regressions nedojdu do produkcie.
Rollout stratégia
Admission controllers sú kritická infraštruktúra. Zlá policy môže zablokovať cluster operations. Fázy:
1. Audit mode (nie Enforce)
# Kyverno: validationFailureAction: Audit (nie Enforce)
# Gatekeeper: enforcementAction: dryrun
Niekoľko týždňov sledujte PolicyReport / gatekeeper-audit. Koľko resources by to blokovalo? Aké false positives?
2. Background scanning
# Kyverno
spec:
background: true # scan existing resources, not just admission
Identifikuje existujúce non-compliant resources. Daju sa opraviť postupne.
3. Enforce po dolaďení
Po 2-4 týždňoch auditu prepnite na enforce. Teraz zamietne nevyhovujúce manifesty.
4. Exclusion list pre system namespaces
spec:
rules:
- name: example
match:
any:
- resources:
kinds: [Pod]
exclude:
any:
- resources:
namespaces: [kube-system, kyverno, gatekeeper-system]
Nikdy neaplikujte policy na kube-system — môžete zablokovať upgrade control plane.
5. Monitoring
Sledujte latency admission webhooks — apiserver_admission_webhook_rejection_count, apiserver_admission_webhook_admission_duration_seconds.
# PrometheusRule
- alert: AdmissionWebhookLatencyHigh
expr: |
histogram_quantile(0.99,
rate(apiserver_admission_webhook_admission_duration_seconds_bucket[5m])
) > 1
for: 5m
annotations:
summary: "Admission webhook p99 latency > 1s — pomalá reakcia API servera"
Anti-patterns
- Start with Enforce mode — vždy audit najprv, inak zablokujete legitímnych workloadov a pokazíte si dôveru tímu.
- Žiadny exclusion pre system namespaces — cluster upgrade zlyhá, kube-scheduler nevie vytvoriť podu.
- Timeout webhooku príliš nízky —
timeoutSeconds: 1spôsobí, že policy zamietne všetko keď je controller pod load. Nastavte 10–30s. - Failure policy Ignore — admission zlyhá, webhook je down, resources sa stále akceptujú. Open admission != secure. Používajte
Fail, ak máte HA deployment. - Monolitický policy súbor — keď máte 50+ rules v jednom
ClusterPolicy, debugging je peklo. Rozdeľte na logické skupiny (security, cost, naming). - Ignorovať RBAC na policies — kto môže meniť
ClusterPolicy? Ak všetci devops, môžu si vypnúť svoje vlastné kontroly. Používajte git + review process, nie ad-hoc kubectl apply. - Žiadne alerty na rejection rate — ak rejection_count klesne na 0, možno policy neplatí (webhook failed-open). Monitor.
- Rego bez unit testov — pri Gatekeeper, Rego kód bez
gator verify= produkčný debugger.
Rozhodovací strom
Potrebujete admission control v K8s?
│
├── Tím hlavne K8s/DevOps, žiadne predošlé Rego znalosti?
│ ├── Potrebujete mutation + generate? → Kyverno
│ ├── Jednoduché validation + Pod Security? → Kyverno (menej boilerplate)
│ └── Všetko ostatné → Kyverno
│
├── Tím už používa OPA mimo K8s (Terraform, API gateways)?
│ └── OPA Gatekeeper (znalosť Rego sa prenáša)
│
├── Potrebujete pokročilú image verifikáciu (Cosign attestations)?
│ └── Kyverno (lepšia natívna podpora cez verifyImages)
│
├── Multi-cluster s complex policies a Rego už používate?
│ └── OPA Gatekeeper
│
└── Default voľba v 2026 pre čisto K8s use-case: **Kyverno**
Kyverno + Gatekeeper spolu?
Technicky môžete mať oba nainštalované. Každý operuje na vlastných CRD typoch, neprekážajú si. Dôvod prečo mať oba:
- Kyverno pre K8s-native policies (najrýchlejší development cycle).
- Gatekeeper pre reusable Rego policy už používaná mimo K8s.
V praxi je to rare setup — duplicitná prevádzková réžia. Väčšina tímov vyberá jeden.
Zhrnutie
Admission control je nutnosť v každom non-trivial K8s klastri. Bez neho closujete zodpovednosť za compliance na ľudí, ktorí si to nebudú pamätať pod tlakom.
V 2026 je Kyverno de-facto standard pre K8s-native tímy — má nižšiu learning curve, lepšie Cosign features, mutation aj generate v jednom nástroji.
OPA Gatekeeper má miesto tam, kde vaša organizácia už investovala do OPA mimo K8s (API authorization, Terraform plans, CI/CD gates). V tom prípade znalosť Rego sa prenáša a konsolidácia tooling-u má zmysel.
V oboch prípadoch: audit pred enforce, testovať cez CLI, monitoring webhook latencie, exkludovať system namespaces. Policy without ops disciplína = produkčný výbuch.