← 19: kube-apiserver | 21: Scheduler →
Три независимых checkpoint
Между TLS handshake и записью в etcd запрос проходит три отдельных checkpoint, каждый со своим вопросом, failure mode и operational risk.
Request
│
├─ Authentication: кто ты? → 401 Unauthorized
│
├─ Authorization: можно ли тебе? → 403 Forbidden
│
└─ Admission: можно ли этот объект? → reject (обычно 403 с деталями)
Admission вызывается только для mutating операций (CREATE, UPDATE, DELETE, CONNECT). GET, LIST, WATCH проходят только authn + authz.
[source: kubernetes.io/docs/reference/access-authn-authz/controlling-access/]
Authentication: кто ты?
Authentication определяет identity запроса. API server проверяет credentials и извлекает username, groups и extra fields. Механизмы проверяются по цепочке — первый успешный побеждает.
[source: kubernetes.io/docs/reference/access-authn-authz/authentication/]
Механизмы аутентификации
Результат аутентификации
Успех:
username: "system:serviceaccount:default:my-sa"
groups: ["system:serviceaccounts", "system:authenticated"]
extra: {}
Провал:
→ 401 Unauthorized (запрос дальше не идёт)
Эта тройка (username, groups, extra) передаётся как userInfo в authorization и admission.
Failure modes
Authorization: можно ли тебе это действие?
Authorization проверяет, имеет ли authenticated user право выполнить конкретное действие над конкретным ресурсом. Модули проверяются по цепочке — первый однозначный ответ побеждает.
[source: kubernetes.io/docs/reference/access-authn-authz/authorization/]
Модули авторизации
RBAC: основной механизм
RBAC оперирует четырьмя объектами:
Role (namespace-scoped) ClusterRole (cluster-scoped)
│ │
│ grants permissions │ grants permissions
│ │
▼ ▼
RoleBinding ClusterRoleBinding
└── привязывает Role └── привязывает ClusterRole
к subject (user/group/SA) к subject (user/group/SA)
Пример Role и RoleBinding:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: app-ns
name: pod-manager
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "create", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: app-ns
name: developer-pod-manager
subjects:
- kind: User
name: developer
roleRef:
kind: Role
name: pod-manager
apiGroup: rbac.authorization.k8s.io
RBAC decision flow
Запрос: user=developer, verb=create, resource=pods, namespace=app-ns
1. Найти все RoleBindings в app-ns, где subject = developer или его group
2. Для каждого binding: проверить linked Role
→ есть rule с verb=create + resource=pods?
3. Найти все ClusterRoleBindings, где subject = developer или его group
4. Для каждого binding: проверить linked ClusterRole
Хотя бы одно совпадение → Allow
Ни одного совпадения → Deny (403 Forbidden)
RBAC — additive only
RBAC работает только на разрешение. Нет explicit deny. Если одна Role разрешает delete pods, другая Role не может это запретить. Единственный способ ограничить — не давать разрешение.
kubectl auth can-i — что проверяет и чего НЕ проверяет
# Проверка authorization layer
kubectl auth can-i create pods -n app-ns
# yes
# Эта команда проверяет authorization layer.
# Она НЕ проверяет:
# - Admission webhooks
# - ResourceQuota
# - LimitRange
# - PodSecurity
# - Custom webhook policies
# "yes" НЕ гарантирует, что create pod пройдёт!
# Проверить все permissions пользователя в namespace
kubectl auth can-i --list -n app-ns
# Проверить от имени другого пользователя (impersonation)
kubectl auth can-i create pods -n app-ns --as=developer
# Проверить от имени ServiceAccount
kubectl auth can-i get secrets -n app-ns \
--as=system:serviceaccount:app-ns:my-sa
[source: kubernetes.io/docs/reference/access-authn-authz/rbac/]
Admission: можно ли принять этот конкретный объект?
Admission — третий и самый сложный checkpoint. В отличие от authn и authz, admission вызывается только для mutating операций и может изменить объект.
Порядок выполнения
Mutating Admission
│ встроенные controllers (LimitRanger, ServiceAccount, ...)
│ + внешние mutating webhooks
│ → могут изменить объект
│
▼
Object Schema Validation
│ проверка структуры объекта после mutations
│
▼
Validating Admission
│ встроенные controllers (ResourceQuota, PodSecurity, ...)
│ + внешние validating webhooks
│ → только accept/reject, без изменений
│
▼
Persist to etcd
Mutating идёт первым, чтобы validating webhooks видели объект после всех mutations.
[source: kubernetes.io/docs/reference/access-authn-authz/admission-controllers/]
Встроенные admission controllers
Встроенные controllers работают in-process — без сетевых вызовов, без external dependency.
Webhook admission: внешние HTTP-сервисы
API server вызывает внешний сервис по HTTP для каждого matching запроса:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: policy-check
webhooks:
- name: validate.policy.example.com
rules:
- apiGroups: [""]
resources: ["pods"]
operations: ["CREATE", "UPDATE"]
clientConfig:
service:
name: policy-webhook
namespace: policy-system
failurePolicy: Fail # ← ключевое решение
timeoutSeconds: 10
sideEffects: None
admissionReviewVersions: ["v1"]
failurePolicy: Fail vs Ignore
Каскадный отказ: deadlock через webhook
Webhook pod на node-1
│
node-1 уходит в drain
│
▼
Webhook pod нужно переместить на другой node
│
▼
Scheduler пытается создать новый Pod → API write
│
▼
API server вызывает webhook → webhook недоступен
│
failurePolicy: Fail → write rejected
│
▼
Webhook не может быть создан, потому что webhook блокирует writes
│
▼
DEADLOCK: кластер read-only
Защита:
- Webhook pods на dedicated nodes с tolerations
- Минимум 2 реплики с podAntiAffinity
namespaceSelector— исключить kube-system из scopetimeoutSecondsне больше 5s- Break-glass:
kubectl delete validatingwebhookconfiguration <name>
[source: kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/]
ValidatingAdmissionPolicy (CEL) — GA в Kubernetes 1.30
Альтернатива validating webhooks без внешних зависимостей:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: require-team-label
spec:
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
resources: ["deployments"]
operations: ["CREATE", "UPDATE"]
validations:
- expression: "has(object.metadata.labels) && 'team' in object.metadata.labels"
message: "Deployment must have 'team' label"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: require-team-label-binding
spec:
policyName: require-team-label
validationActions: [Deny]
CEL expressions выполняются in-process в API server. Нет network call, нет external dependency, нет availability risk. Ограничение: только validation (нет mutation), только CEL (нет произвольной логики).
[source: kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/]
Сигналы мониторинга
Authentication
Authorization
Admission
Когда использовать
RBAC:
- Всегда. RBAC включён по умолчанию с Kubernetes 1.8+. Нет причин отключать.
- Для сервисных аккаунтов: минимально необходимые permissions, не cluster-admin по умолчанию.
- ClusterRole используйте только когда реально нужен cluster-wide доступ.
Webhook admission:
- Когда нужна логика, которую нельзя выразить в CEL (внешние вызовы, сложные условия).
- Когда нужна mutation (изменение объектов): ValidatingAdmissionPolicy не умеет.
- OPA/Gatekeeper, Kyverno — типичные use cases.
ValidatingAdmissionPolicy (CEL):
- Предпочтительно для новых policy в k8s 1.30+. Нет availability risk от внешнего webhook.
- Простые правила: проверка labels, annotations, resource limits.
- Не подходит для: mutation, внешних lookups, сложной бизнес-логики.
OIDC:
- Когда нужен централизованный IdP для пользователей (Keycloak, Dex, Google Workspace).
- Позволяет делать kubectl через SSO без распространения сертификатов.
Типичные ошибки
1. Путать kubectl auth can-i с реальной проверкой
can-i проверяет authorization layer через SelfSubjectAccessReview, но не admission. ResourceQuota, LimitRange, webhook policies и PodSecurity не проверяются. Можно получить yes и всё равно получить rejection при apply.
2. Широкие ClusterRoleBindings
cluster-admin для ServiceAccount оператора — типичная ошибка. Оператор нужен доступ к конкретным ресурсам, не ко всему кластеру. Используйте минимальные Roles.
3. Один инстанс webhook с failurePolicy: Fail
Гарантированный путь к deadlock при рестарте webhook. Минимум 2 реплики + podAntiAffinity + namespaceSelector, исключающий system namespaces.
4. Не контролировать apiserver_admission_webhook_fail_open_count
Если failurePolicy: Ignore и webhook деградировал — он молча пропускает все запросы без проверки. Метрика fail_open_count > 0 означает, что политика не работает.
5. ServiceAccount tokens без expiry
До k8s 1.24 SA tokens были вечными. Сейчас projected service account tokens истекают через 1 час по умолчанию и автоматически ротируются. Старые workloads, монтирующие /var/run/secrets/kubernetes.io/serviceaccount/token напрямую, могут сломаться.
Альтернативы
Вместо webhook-based authorization: ValidatingAdmissionPolicy (CEL) для stateless validation без external dependency. OPA Gatekeeper и Kyverno используют webhooks, но дают policy-as-code абстракцию (Kyverno проще, Gatekeeper мощнее).
Вместо X.509 cert для пользователей: OIDC + oidc-login plugin для kubectl — централизованный revocation, MFA, группы из IdP, без ручной ротации сертификатов.
Вместо ручного RBAC: GitOps (ArgoCD + RBAC manifests в git) или rbac-manager (operator) для управления RoleBindings.
[source: kubernetes.io/docs/concepts/security/rbac-good-practices/]