закреплять стандарты кластера до записи объектов в etcd
Admission controllers в Kubernetes: политики и управление конфигурацией
13 минут
Плохие манифесты попадают в etcd, если на пути admission ничего нет. Сочетайте ValidatingAdmissionPolicy на CEL, движки политик и аккуратно обслуживаемые mutating/validating webhook — с выкатом Ignore→Fail, селекторами namespace и TLS через cert-manager — чтобы дефолты и запреты срабатывали до сохранения.
Почему ошибочные конфигурации всё равно попадают в кластер
Многие инциденты, которые списывают на «странности Kubernetes», начинаются с манифестов, которые не должны были пройти: без лимитов CPU и памяти, от root, с hostPath, с образами из случайных реестров, без меток владельца или cost-center. Вики и код-ревью не отклоняют create. Встроенные контроллеры вроде LimitRanger и NamespaceLifecycle помогают, но не кодируют ваш allowlist реестров или требование probes. Admission — узкое место между API-сервером и etcd после аутентификации и авторизации: здесь задают дефолты и проверяют политики до записи. Без осознанной стратегии admission каждый namespace изобретает свои правила, а аудит превращается в раскопки.
Цепочка admission в правильном порядке
Сначала запрос проходит аутентификацию и авторизацию (RBAC или другой authorizer). Только потом работает admission. Mutating admission может изменить объект — подставить дефолты, метки или sidecar. Далее — проверка схемы объекта. Затем validating admission принимает или отклоняет итоговый объект; validating webhook и ValidatingAdmissionPolicy работают на этом этапе и не должны мутировать. Если все проверки прошли, объект пишется в etcd. Разделяйте роли: mutate — безопасные дефолты, validate — жёсткие запреты. Не оставляйте бизнес-политики только в CI — и GitOps, и kubectl при правильной настройке бьют в один и тот же путь admission.
Выбор слоя принуждения: CEL, движки или свои webhook
Берите самый лёгкий инструмент, который решает задачу. ValidatingAdmissionPolicy считает CEL внутри API-сервера — без пода webhook, с меньшей задержкой, GA с Kubernetes 1.30. Удобен для обязательных меток, потолка реплик и простых проверок securityContext. Kyverno или OPA Gatekeeper — когда нужны богатая мутация, generate, проверка подписи образов, отчёты аудита или Rego за пределами кластера; эти движки всё равно регистрируются как admission webhook. Свой webhook пишите только если логику не выразить CEL и движком или нужно вызвать внутренний сервис на пути admission. Свой код — это TLS, доступность и риск обновлений; большинство платформ должны исчерпать VAP и движок политик, прежде чем выкатывать обработчики на Go.
Нативная проверка через ValidatingAdmissionPolicy
Опишите логику политики один раз, затем привяжите её через ValidatingAdmissionPolicyBinding и validationActions: Deny, Warn или Audit. Начните с Audit или Warn на помеченных namespace, поправьте чарты, затем переключите на Deny. failurePolicy Fail включайте только после проверки выражений — ошибочный CEL с Fail блокирует подходящие запросы. Исключайте kube-system и namespace контроллеров из matchConstraints.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: workload-baseline
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: "object.metadata.labels.?team.hasValue()"
message: "Pod must have label team"
- expression: >-
object.spec.containers.all(c,
c.?securityContext.?runAsNonRoot == optional.of(true) ||
c.?securityContext.?runAsUser.orValue(0) > 0)
message: "Containers must not run as root"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: workload-baseline-apps
spec:
policyName: workload-baseline
validationActions: ["Deny"]
matchResources:
namespaceSelector:
matchLabels:
admission.policy/enforce: "true"Mutating webhook для дефолтов — обслуживайте как продакшен-сервис
Если мутации не хватает LimitRange и mutator'ов движка, зарегистрируйте MutatingWebhookConfiguration с sideEffects None, явным timeoutSeconds и opt-in через namespaceSelector. TLS отдавайте через cert-manager и инжектируйте CA в caBundle аннотацией, чтобы ротация не отрезала API-сервер. На canary-namespace начните с failurePolicy Ignore; на Fail переходите только при двух репликах, пробах и дашбордах. reinvocationPolicy IfNeeded — если поздние mutator'ы требуют второго прохода. Обработчики делайте идемпотентными — не добавляйте второй sidecar на UPDATE. Лучше один процесс с /mutate и /validate на одном порту, чем два контейнера, конфликтующих за 8443.
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: config-defaults
annotations:
cert-manager.io/inject-ca-from: platform-system/webhook-tls
webhooks:
- name: defaults.platform.example.com
admissionReviewVersions: ["v1"]
sideEffects: None
timeoutSeconds: 5
failurePolicy: Ignore
matchPolicy: Equivalent
reinvocationPolicy: IfNeeded
clientConfig:
service:
name: admission-webhooks
namespace: platform-system
path: /mutate
port: 443
rules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
scope: Namespaced
namespaceSelector:
matchLabels:
admission-config-defaults: "enabled"
objectSelector:
matchExpressions:
- key: admission.policy/skip
operator: DoesNotExistapiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: webhook-tls
namespace: platform-system
spec:
secretName: webhook-tls
duration: 2160h
renewBefore: 360h
issuerRef:
name: webhook-issuer
kind: Issuer
usages: ["server auth"]
dnsNames:
- admission-webhooks.platform-system.svc
- admission-webhooks.platform-system.svc.cluster.localValidating-запреты: реестры, probes и privileged
Жёсткие правила выражайте как validation — CEL, Kyverno/Gatekeeper или validating webhook. Типовой минимум: запрет privileged и hostPath, обязательный readiness у подов публичных Service, образы только из корпоративного реестра. Не считайте docker.io/library доверенным по умолчанию в продакшене. Проверяйте через kubectl --dry-run=server, чтобы admission отработал без записи. Если свой validating webhook неизбежен, возвращайте Allowed false с понятным Message; логируйте пользователя, namespace, GVK и решение для аудита и SIEM.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: policy-validate
annotations:
cert-manager.io/inject-ca-from: platform-system/webhook-tls
webhooks:
- name: validate.platform.example.com
admissionReviewVersions: ["v1"]
sideEffects: None
timeoutSeconds: 5
failurePolicy: Ignore
clientConfig:
service:
name: admission-webhooks
namespace: platform-system
path: /validate
port: 443
rules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
namespaceSelector:
matchLabels:
admission.policy/enforce: "true"#!/usr/bin/env bash
set -euo pipefail
kubectl label namespace app-team admission.policy/enforce=true --overwrite
kubectl apply --dry-run=server -f bad-pod-privileged.yaml
# Expect: admission webhook or ValidatingAdmissionPolicy denial
kubectl apply --dry-run=server -f good-pod.yamlОбслуживайте admission как сервис на критическом пути
Задержка admission — это задержка API: алертируйте на P95 webhook и долю ошибок, а также на метрики apiserver_admission_webhook_* у API-сервера. Держите у Deployment webhook минимум две реплики и PodDisruptionBudget. Не включайте Fail на весь кластер в первый день — расширяйте namespaceSelector постепенно. Версионируйте политики в Git рядом с конфигом кластера и ревьюйте как код приложений. Согласуйте с мультитенантными квотами, чтобы LimitRange, ResourceQuota и отказы admission рассказывали одну историю. Admission controllers — последний шлюз перед состоянием кластера: предпочитайте нативный CEL и зрелые движки, свои webhook оставляйте для настоящих исключений, а Ignore поднимайте до Fail только когда наблюдаемость подтверждает, что путь безопасен.
Библиотеки политик Kyverno и Gatekeeper работают на том же пути admission из гайда по Policy as Code с OPA Gatekeeper и Kyverno.
Admission-контроль продолжает базовую линию из гайда по ужесточению безопасности Kubernetes для production-кластеров.
