← Back to notes

Authentication, Authorization и
Admission в Kubernetes


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

Защита:

  1. Webhook pods на dedicated nodes с tolerations
  2. Минимум 2 реплики с podAntiAffinity
  3. namespaceSelector — исключить kube-system из scope
  4. timeoutSeconds не больше 5s
  5. 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/]


19: kube-apiserver | 21: Scheduler

Authentication, Authorization и Admission в Kubernetes | Aleksandr Suprun