Стадия жизненного цикла модуля: General Availability
Как создать пользователя?
Как ограничить права пользователю конкретными неймспейсами?
Чтобы ограничить права пользователя конкретными неймспейсами в гранулярной ролевой модели, используйте в RoleBinding namespace-роль с соответствующим уровнем доступа. Пример.
В упрощённой ролевой модели используйте параметры namespaceSelector или limitNamespaces (устарел) в кастомном ресурсе ClusterAuthorizationRule.
Что, если два ClusterAuthorizationRules подходят для одного пользователя?
В примере пользователь jane.doe@example.com состоит в группе administrators. Созданы два ClusterAuthorizationRules:
apiVersion: deckhouse.io/v1
kind: ClusterAuthorizationRule
metadata:
name: jane
spec:
subjects:
- kind: User
name: jane.doe@example.com
accessLevel: User
namespaceSelector:
labelSelector:
matchLabels:
env: review
---
apiVersion: deckhouse.io/v1
kind: ClusterAuthorizationRule
metadata:
name: admin
spec:
subjects:
- kind: Group
name: administrators
accessLevel: ClusterAdmin
namespaceSelector:
labelSelector:
matchExpressions:
- key: env
operator: In
values:
- prod
- stage
jane.doe@example.comимеет право запрашивать и просматривать объекты среди всех пространств имён, помеченныхenv=review.Administratorsмогут запрашивать, редактировать, получать и удалять объекты на уровне кластера и из пространств имён, помеченныхenv=prodиenv=stage.
Так как для Jane Doe подходят два правила, итоговые права определяются следующим образом:
Jane Doeбудет иметь максимальный accessLevel среди всех подходящих правил —ClusterAdmin.- Опции
namespaceSelectorбудут объединены так, чтоJane Doeбудет иметь доступ в пространства имён, помеченные лейбломenvсо значениемreview,stageилиprod.
Если есть правило без опции namespaceSelector и без опции limitNamespaces (устаревшая), это значит, что доступ разрешён во все пространства имён, кроме системных, что повлияет на результат вычисления доступных пространств имён для пользователя.
Можно ли использовать упрощённую и гранулярную ролевые модели одновременно?
Да. Обе модели в итоге сводятся к стандартному механизму RBAC Kubernetes, а RBAC — разрешающая модель: права из всех источников суммируются. Если действие разрешено хотя бы одним источником — ClusterAuthorizationRule, AuthorizationRule, RoleBinding на роль гранулярной модели или ProjectRoleBinding, — оно будет разрешено. Ничего специально «переключать» не нужно: можно оставить существующие ClusterAuthorizationRule и постепенно добавлять привязки ролей гранулярной модели.
Это верно и в режиме мультитенантности (enableMultiTenancy), с одним правилом об уровнях. Ограничение неймспейсов в ClusterAuthorizationRule (limitNamespaces или namespaceSelector) ограничивает права только этого правила: в неймспейсе вне списка пользователь сохраняет то, что ему дают RoleBinding, AuthorizationRule или ProjectRoleBinding, на уровне этой привязки — accessLevel из ClusterAuthorizationRule туда не распространяется. Неймспейс, в котором у пользователя нет ни одного источника прав, остаётся недоступным. Полный список источников и пример — в описании модуля.
Как обновление на релиз с контроллером влияет на биндинги?
Ничего не создаётся заново, доступ не прерывается. Компонент user-authz-controller принимает под управление биндинги, которые раньше создавал Helm-чарт: те же объекты с теми же именами, к ним добавляется ownerReference на правило.
Спланируйте релиз, в котором выполняется миграция: он один раз обрабатывает все существующие биндинги, поэтому идёт дольше обычного. На последующие релизы это не влияет.
| Биндинги в релизе | Чего ожидать |
|---|---|
| До 5000 | Релиз идёт дольше обычного и завершается сам |
| Больше 5000 | Релиз может не уложиться в 20-минутный таймаут модуля и завершиться с ошибкой |
Посчитайте биндинги перед обновлением. Для получения количества кластерных биндингов используйте команду:
d8 k get clusterrolebindings -l heritage=deckhouse,module=user-authz --no-headers | wc -l
Для получения количества namespaced-биндингов используйте команду:
d8 k get rolebindings -A -l heritage=deckhouse,module=user-authz --no-headers | wc -l
Если их больше 5000, на время обновления увеличьте таймаут релиза или переключитесь на предыдущий движок раскатки, а после обновления верните настройку:
Чтобы увеличить таймаут релиза, используйте команду:
d8 k -n d8-system set env deploy/deckhouse HELM_TIMEOUT=60m
Чтобы переключиться на предыдущий движок, используйте команду:
d8 k -n d8-system set env deploy/deckhouse USE_NELM=false
Если миграция не смогла подготовить все биндинги, релиз не стартует, а биндинги остаются в прежнем состоянии.
Как проверить, что контроллер поддерживает биндинги в актуальном состоянии?
Компонент user-authz-controller синхронизирует ClusterRoleBinding и RoleBinding каждого ClusterAuthorizationRule и AuthorizationRule, выдачу d8:use:dict в экспериментальной ролевой модели и проекции manage-ролей в use-RoleBinding’и неймспейсов. О его состоянии говорят три источника (статус объекта, метрики и алерты).
Статус объекта. У каждого правила есть условие Ready и число созданных для него ClusterRoleBinding и RoleBinding:
Чтобы получить список всех ClusterAuthorizationRule в кластере, выполните команду:
d8 k get clusterauthorizationrules
Пример вывода:
NAME ACCESS LEVEL READY BINDINGS AGE
my-rule Admin True 3 5d
Чтобы получить список всех AuthorizationRule во всех неймспейсах кластера, выполните команду:
d8 k get authorizationrules -A
Пример вывода:
NAMESPACE NAME ACCESS LEVEL READY BINDINGS AGE
default app-rule Editor True 2 3d
team-a dev-rule User False 0 1h
Чтобы получить только массив conditions указанного ClusterAuthorizationRule, выполните команду:
d8 k get clusterauthorizationrule <name> -o jsonpath='{.status.conditions}'
Пример вывода:
[
{
"lastTransitionTime": "2026-09-11T10:00:00Z",
"message": "3 bindings applied",
"observedGeneration": 1,
"reason": "BindingsApplied",
"status": "True",
"type": "Ready"
}
]
Ready=True с причиной BindingsApplied означает, что биндинги соответствуют правилу. Ready=False указывает на проблему: InvalidSpec (правило нельзя превратить в биндинги, в сообщении сказано почему) или ApplyError (API-сервер отклонил запись; в сообщении указаны биндинг и ошибка). На правиле записываются события с теми же причинами.
Метрики. Контроллер отдаёт их на порту metrics своего пода, собирает их PodMonitor user-authz-controller (нужен включённый модуль operator-prometheus). Лейбл kind принимает значения ClusterAuthorizationRule, AuthorizationRule, dict и manage.
| Метрика | Описание |
|---|---|
d8_user_authz_authorization_rules{kind} |
Известные контроллеру объекты данного вида |
d8_user_authz_bindings_desired{kind} |
Биндинги, которые должны быть у объектов данного вида |
d8_user_authz_bindings_actual{kind} |
Существующие биндинги данного вида |
d8_user_authz_bindings_drift{kind,reason} |
Биндинги, не приведённые к желаемому состоянию на последнем reconcile; reason — missing, extra или changed. Больше нуля, пока контроллер не привёл биндинги к желаемому состоянию |
d8_user_authz_bindings_apply_total{kind,op,result} |
Операции записи контроллера (op: create, update, delete; result: success, error) |
d8_user_authz_authorization_rules_invalid{kind,reason} |
Правила с условием Ready=False по виду и причине |
d8_user_authz_authorization_rule_invalid{kind,name,rule_namespace,reason} |
1 для каждого такого правила (по имени экспортируется не более 50 правил; агрегат выше всегда полный) |
d8_user_authz_custom_cluster_roles{level} |
Кастомные ClusterRole (с аннотацией user-authz.deckhouse.io/access-level) по уровням доступа |
d8_user_authz_custom_aggregation_missing{level} |
1, если у агрегированной ClusterRole user-authz:<level>:custom нет правил, хотя кастомные роли для неё существуют |
d8_user_authz_bindings_keep_stamped_total, d8_user_authz_keep_stamp_duration_seconds |
Работа миграционного хука, защищающего созданные чартом биндинги от удаления релизом: сколько объектов помечено и длительность последнего прогона |
controller_runtime_reconcile_total, controller_runtime_reconcile_errors_total, controller_runtime_reconcile_time_seconds |
Стандартные метрики controller-runtime по каждому контроллеру (лейбл controller: clusterauthorizationrule-bindings, authorizationrule-bindings, dict-bindings, manage-bindings) |
Алерты (в правилах Prometheus d8_user_authz):
D8UserAuthzControllerUnavailableиD8UserAuthzControllerTargetDown— у контроллера недоступные реплики или его метрики не собираются 5 минут;D8UserAuthzControllerReconcileErrorsHigh— устойчивый поток ошибок reconcile;D8UserAuthzBindingsDrift— биндинги какого-то вида 15 минут не приводятся к желаемому состоянию;D8UserAuthzAuthorizationRulesInvalidиD8UserAuthzAuthorizationRuleInvalid— число и имена правил, биндинги которых не применяются 10 минут;D8UserAuthzCustomRolesNotAggregated— агрегированная ClusterRole уровня доступа остаётся пустой, хотя кастомные роли для неё есть.
Если правило остаётся в Ready=False с ApplyError из-за того, что у биндинга вручную изменили roleRef, удалите этот биндинг: roleRef неизменяем, контроллер пересоздаст биндинг с правильным.
Как проверить, что вебхук авторизации знает актуальные правила?
Вебхук авторизации и Permission Browser читают параметры мультитенантности из ClusterAuthorizationRules (limitNamespaces, namespaceSelector, allowAccessToSystemNamespaces) напрямую из API, поэтому изменение доходит до них за несколько секунд.
Чтобы проверить, что поды вебхука запущены, выполните команду:
d8 k -n d8-user-authz get pods -l app=user-authz-webhook -o wide
Пример вывода (по одному поду на каждый master, в сети узла; готовы должны быть оба контейнера):
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
user-authz-webhook-qrm2n 2/2 Running 0 2d 10.0.0.11 master-0 <none> <none>
user-authz-webhook-h6jbh 2/2 Running 0 2d 10.0.0.12 master-1 <none> <none>
user-authz-webhook-97fvs 2/2 Running 0 2d 10.0.0.13 master-2 <none> <none>
Чтобы вывести последние 100 строк логов контейнера вебхука из всех таких подов, выполните команду:
d8 k -n d8-user-authz logs -l app=user-authz-webhook -c webhook --tail=100
Пример вывода:
2026/09/11 10:00:00 server is starting to listen on 127.0.0.1:40443 ...
2026/09/11 10:00:01 rules source: directory rebuilt from 12 rules (18 subjects, 0 quarantined) in 4.2ms
Строка rules source: directory rebuilt from N rules показывает, что informer получил список правил, и сколько их в каталоге; quarantined — число правил, у которых не скомпилировались шаблоны limitNamespaces.
Экземпляр, который ещё не прочитал правила, не готов и отвечает на запросы авторизации ошибкой, а не запретом. В те секунды, пока созданное правило не дошло до вебхука, его субъектам временно запрещён доступ ко всем неймспейсам.
Метрики вебхук отдаёт через сайдкар-контейнер kube-rbac-proxy; их собирает PodMonitor user-authz-webhook, для этого должен быть включён модуль operator-prometheus. На каждый master-узел приходится своя серия.
| Метрика | Описание |
|---|---|
user_authz_webhook_rules_informer_synced |
1, если экземпляр хотя бы раз прочитал ClusterAuthorizationRules |
user_authz_webhook_rules_observed |
Правила, которые экземпляр использует сейчас |
user_authz_webhook_rules_subjects |
Число различных субъектов в этих правилах |
user_authz_webhook_rules_max_resource_version |
Наибольший resourceVersion среди прочитанных правил. Сравните его с кластером, чтобы измерить отставание |
user_authz_webhook_rules_quarantined |
Правила, которые экземпляр не смог использовать полностью: не компилируется паттерн limitNamespaces или namespaceSelector либо правило не удалось прочитать |
user_authz_webhook_rules_directory_updated_timestamp_seconds |
Время последнего обновления правил |
user_authz_webhook_rules_directory_rebuilds_total, user_authz_webhook_rules_directory_rebuild_duration_seconds |
Число обновлений правил и их длительность |
user_authz_webhook_rules_watch_errors_total |
Ошибки list и watch при чтении правил |
Permission Browser отдаёт те же метрики с префиксом user_authz_permission_browser; их собирает PodMonitor permission-browser-apiserver.
Для обоих компонентов модуль поставляет следующие алерты:
| Алерт | Когда срабатывает |
|---|---|
D8UserAuthzWebhookTargetDown |
Prometheus 5 минут не может собрать метрики хотя бы с одного экземпляра вебхука |
D8UserAuthzWebhookRulesQuarantined |
Правило 10 минут не компилируется |
D8UserAuthzWebhookRulesWatchErrors |
Вебхук 10 минут не может следить за изменениями правил |
D8UserAuthzWebhookDirectoryDiverged |
Экземпляры 10 минут используют разные наборы правил, поэтому на один и тот же запрос приходит разный ответ в зависимости от master-узла, на который он попал |
D8UserAuthzRulePropagationLag |
Один экземпляр час не обновлял правила, а другой обновлял |
D8UserAuthzPermissionBrowserUnavailable |
У Permission Browser есть недоступные реплики |
Почему изменение ClusterAuthorizationRule применяется не сразу?
API-сервер кеширует ответы вебхука авторизации. В ресурсе AuthorizationConfiguration, который создаёт control-plane-manager, для вебхука заданы authorizedTTL: 5m, unauthorizedTTL: 30s и timeout: 3s. Ключ кеша — весь SubjectAccessReview, поэтому ответ на повторный идентичный запрос приходит из кеша.
Вебхук никогда не разрешает запрос: он либо запрещает его, либо не имеет мнения, и оба ответа API-сервер кеширует на unauthorizedTTL, то есть на 30 секунд. Поэтому создание правила или его сужение применяется к конкретному запросу в течение 30 секунд с момента последнего идентичного запроса:
d8 k auth can-i --as=user@example.com get pods -n other-namespace
sleep 30
d8 k auth can-i --as=user@example.com get pods -n other-namespace
Сбросить кеш без перезапуска kube-apiserver нельзя.
Для ресурса только что установленного CRD, задержка достигает 40 секунд. Вебхук узнаёт из discovery, принадлежит ли ресурс неймспейсу, и запрашивает discovery не чаще раза в 10 секунд, после чего API-сервер кеширует ответ ещё на 30 секунд.
В эти секунды ограничения по неймспейсам к такому ресурсу не применяются: вебхук о нём ещё не знает, мнения не имеет, и отвечает один только RBAC — поэтому субъект, которого правило ограничивает несколькими неймспейсами, может запросить новый ресурс по всему кластеру. Установка CRD требует значительно более широких прав, чем может предоставить это окно, поэтому discovery запрашивается с ограниченной частотой, а не на каждый запрос. У Permission Browser есть такое же по порядку окно для его отчёта — до 30 секунд.
Почему правило требует мультитенантности?
Параметры limitNamespaces, namespaceSelector и allowAccessToSystemNamespaces применяет вебхук авторизации, а он разворачивается, только если включён параметр enableMultiTenancy. Правило, в котором эти параметры заданы при выключенной мультитенантности, применяется без них: субъекты правила получают его уровень доступа во всех неймспейсах кластера, включая системные.
В метриках учитываются только параметры, которые задают ограничение. Правило с allowAccessToSystemNamespaces: false или с пустым списком limitNamespaces в метрики не попадает.
О ситуации сообщают две метрики:
| Метрика | Описание |
|---|---|
d8_user_authz_rule_needs_multitenancy{name,options} |
Одна серия на каждое затронутое правило, не более 50, с параметрами, которые не применятся |
d8_user_authz_rules_needing_multitenancy |
Общее число затронутых правил, включая те, что не попали в первые 50 |
Алерт D8UserAuthzRuleNeedsMultiTenancy называет отдельное правило, а D8UserAuthzRulesNeedMultiTenancy срабатывает, когда затронутых правил больше, чем называет первый алерт. Оба входят в группу D8UserAuthzMisconfigured.
Чтобы устранить ситуацию, включите мультитенантность или уберите параметры из правил.
Чтобы посмотреть, включена ли мультитенантность, используйте команду:
d8 k get moduleconfig user-authz -o jsonpath='{.spec.settings.enableMultiTenancy}'
Чтобы получить имена ClusterAuthorizationRule, которые ограничивают доступ по неймспейсу или разрешают доступ к системным неймспейсам, используйте команду:
d8 k get clusterauthorizationrule -o json | jq -r '.items[] | select((.spec.limitNamespaces // [] | length > 0) or (.spec.namespaceSelector != null) or (.spec.allowAccessToSystemNamespaces == true)) | .metadata.name'
Как получить аналог ролей ClusterAdmin и SuperAdmin в гранулярной модели?
В гранулярной модели нет ролей, объединяющих права в одну сущность, как ClusterAdmin и SuperAdmin в упрощённой модели. В ней разделяется управление платформой (системные роли) и доступ к приложениям (namespace- и проектные роли). Эквивалент собирается из двух привязок: ClusterRoleBinding на системную роль и ClusterProjectRoleBinding на проектную роль (она действует во всех проектах, включая создаваемые позже).
Приблизительное соответствие уровней:
| Роль упрощённой модели | Эквивалент в гранулярной модели |
|---|---|
User |
d8:namespace:viewer (через RoleBinding или ProjectRoleBinding). |
PrivilegedUser |
d8:namespace:user. |
Editor |
d8:namespace:manager — тот же уровень; отличается область: роль гранулярной модели выдаётся на неймспейс (RoleBinding) или на проект (ProjectRoleBinding), а не на весь кластер с фильтром по неймспейсам. |
Admin |
d8:namespace:admin. |
ClusterEditor |
Прямого аналога нет. ClusterEditor — уровень Editor во всех неймспейсах, включая системные, плюс cluster-scoped-объекты. Собирается из ClusterProjectRoleBinding на d8:project:manager (все проекты) и системной роли для платформенной части; d8:system:manager сам по себе эквивалентом не является: доступа к пользовательским неймспейсам он не даёт. |
ClusterAdmin |
d8:system:manager + ClusterProjectRoleBinding на d8:project:admin. |
SuperAdmin |
d8:system:superadmin + ClusterProjectRoleBinding на d8:project:superadmin. |
Пример для ClusterAdmin (группа k8s-admins):
# Платформа: конфигурация модулей DP, cluster-wide-ресурсы, системные неймспейсы.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: k8s-admins-platform
subjects:
- kind: Group
name: k8s-admins
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: d8:system:manager
apiGroup: rbac.authorization.k8s.io
---
# Приложения: администратор во всех неймспейсах всех проектов (включая будущие).
apiVersion: deckhouse.io/v1alpha3
kind: ClusterProjectRoleBinding
metadata:
name: k8s-admins-projects
spec:
subjects:
- kind: Group
name: k8s-admins
roleRef:
kind: ClusterRole
name: d8:project:admin
Для SuperAdmin замените роли на d8:system:superadmin и d8:project:superadmin.
Особенности:
- При включённом модуле
multitenancy-managerкаждый пользовательский неймспейс является проектом, поэтому пара «системная роль + ClusterProjectRoleBinding» покрывает и платформу, и все пользовательские неймспейсы. Не покрывается только неймспейсdefault— он не относится ни к проектам, ни к системным. - Создать собственную роль «со всеми правами» (
apiGroups: ["*"], resources: ["*"], verbs: ["*"]) не получится: такая роль даёт, в том числе права на управление проектами и будет отклонена встроенной защитой. Если нужен именно неограниченный доступ ко всему API (вне ролевой модели платформы), используйте ClusterRoleBinding на встроенную роль Kubernetescluster-admin— назначить её может только тот, у кого такие права уже есть.
Как дать пользователю доступ только к ресурсам одного модуля?
Типовой запрос: пользователь в неймспейсе должен работать только с ресурсами одного модуля (например, только с виртуальными машинами), не видя остальных ресурсов (Pod, Deployment и т. п.).
Каждый модуль DP поставляет отдельные capabilities на свои ресурсы, поэтому такой доступ выдаётся без написания RBAC-правил. Соберите собственную роль, агрегирующую только capabilities нужного модуля (селектор по лейблу module):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: d8:custom:namespace:virtualization-only
labels:
rbac.deckhouse.io/kind: custom-role
rbac.deckhouse.io/scope: namespace
rbac.deckhouse.io/delegatable: "true" # Разрешает использовать роль в RoleBinding внутри проектов.
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.deckhouse.io/kind: capability
rbac.deckhouse.io/scope: namespace
module: virtualization
rules: []
Выдайте роль через RoleBinding в нужном неймспейсе или через ProjectRoleBinding на весь проект. Пользователь получит доступ только к ресурсам модуля — стандартные ресурсы Kubernetes ему видны не будут.
Вне проектов то же самое можно сделать, привязав capability модуля напрямую, без создания роли:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: virtualization-view
namespace: my-namespace
subjects:
- kind: User
name: user@example.com
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: d8:namespace-capability:virtualization:view
apiGroup: rbac.authorization.k8s.io
Внутри неймспейсов проектов обычный RoleBinding может ссылаться только на роли, доступные проекту, — capabilities туда по умолчанию не входят, поэтому для проектов используйте вариант с собственной ролью (лейбл rbac.deckhouse.io/delegatable: "true" в примере выше как раз делает её доступной) либо ProjectRoleBinding.
Как расширить роль или создать новую?
Гранулярная ролевая модель построена на принципе агрегации, она собирает более мелкие роли в более обширные, тем самым предоставляя лёгкие способы расширения модели собственными ролями.
Создание новой роли подсистемы
Предположим, что текущие подсистемы не подходят под ролевое распределение в компании и требуется создать новую подсистему,
которая будет включать в себя роли из подсистемы deckhouse, подсистемы kubernetes и модуля user-authn.
Для решения этой задачи создайте следующую роль:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: d8:custom:mycustom:manager
labels:
rbac.deckhouse.io/use-role: admin
rbac.deckhouse.io/kind: custom-role
rbac.deckhouse.io/scope: subsystem
rbac.deckhouse.io/subsystem: mycustom
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.deckhouse.io/aggregate-to-deckhouse-as: manager
- matchLabels:
rbac.deckhouse.io/aggregate-to-kubernetes-as: manager
- matchLabels:
rbac.deckhouse.io/scope: system
module: user-authn
rules: []
В начале указаны лейблы для новой роли:
-
показывает, какую namespace-роль хук должен использовать при создании RoleBinding в неймспейсах модулей:
rbac.deckhouse.io/use-role: admin -
показывает, что роль является кастомной (кастомные роли не определяют собственных правил, а только агрегируют capabilities):
rbac.deckhouse.io/kind: custom-roleЭтот лейбл обязателен.
-
показывает, что роль является ролью подсистемы, и обрабатываться будет соответственно:
rbac.deckhouse.io/scope: subsystem -
указывает подсистему, за которую отвечает роль:
rbac.deckhouse.io/subsystem: mycustom
Далее указаны селекторы, именно они реализуют агрегацию:
-
агрегирует роль менеджера из подсистемы
deckhouse:rbac.deckhouse.io/aggregate-to-deckhouse-as: manager -
агрегирует все системные (scope
system) capabilities модуляuser-authn:rbac.deckhouse.io/scope: system module: user-authn
Таким образом роль получает права от подсистем deckhouse, kubernetes и от модуля user-authn.
Особенности:
- кастомные роли и capabilities должны иметь префикс имени
d8:custom:(остальное пространство имёнd8:зарезервировано за встроенными объектами DP). Имя должно согласовываться с объявленной областью: подсистемная роль —d8:custom:<подсистема>:<имя>(сегмент — сама подсистема, как в примере выше), namespace- или проектная роль —d8:custom:namespace:<имя>иd8:custom:project:<имя>, capability —d8:custom:<область>-capability:<имя>. Имя, расходящееся с лейбломrbac.deckhouse.io/scope, будет отклонено; - RoleBinding с namespace-ролью (
d8:namespace:<уровень>) будут созданы в неймспейсах модулей агрегированных подсистем, уровень задаётся лейбломrbac.deckhouse.io/use-role; - кастомную роль нельзя агрегировать в
d8:system:<уровень>, потому что лейблrbac.deckhouse.io/aggregate-to-system-asможет нести только кастомная capability. Чтобы выдать роль обладателямd8:system:manager, привяжите её к ним через ClusterRoleBinding.
Расширение пользовательской роли
Например, в кластере появился новый кластерный (пример для manage-роли) CRD-объект — MySuperResource, и нужно дополнить собственную роль из примера выше правами на взаимодействие с этим ресурсом.
Первым делом нужно дополнить роль новым селектором:
rbac.deckhouse.io/aggregate-to-mycustom-as: manager
Этот селектор позволит агрегировать capabilities к новой подсистеме через указание этого лейбла. После добавления нового селектора роль будет выглядеть так:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: d8:custom:mycustom:manager
labels:
rbac.deckhouse.io/use-role: admin
rbac.deckhouse.io/kind: custom-role
rbac.deckhouse.io/scope: subsystem
rbac.deckhouse.io/subsystem: mycustom
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.deckhouse.io/aggregate-to-deckhouse-as: manager
- matchLabels:
rbac.deckhouse.io/aggregate-to-kubernetes-as: manager
- matchLabels:
rbac.deckhouse.io/scope: system
module: user-authn
- matchLabels:
rbac.deckhouse.io/aggregate-to-mycustom-as: manager
rules: []
Далее нужно создать новую capability, в которой следует определить права для нового ресурса. Например, только чтение:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
rbac.deckhouse.io/aggregate-to-mycustom-as: manager
rbac.deckhouse.io/kind: custom-capability
rbac.deckhouse.io/scope: subsystem
rbac.deckhouse.io/subsystem: mycustom
rbac.deckhouse.io/capability: "custom.subsystem-capability.mycustom.superresource_view"
name: d8:custom:subsystem-capability:mycustom:superresource:view
rules:
- apiGroups:
- mygroup.io
resources:
- mysuperresources
verbs:
- get
- list
- watch
Capability добавит свои права в роль подсистемы, дав права на просмотр нового объекта.
Особенности:
- кастомные capabilities должны иметь префикс имени
d8:custom:; остальная часть имени не ограничена, но для читаемости лучше использовать этот стиль.
Расширение существующих подсистемных ролей
Если необходимо расширить существующую роль, нужно выполнить те же шаги, что и в пункте выше, но изменив лейблы и название роли.
Пример для расширения роли менеджера из подсистемы deckhouse (d8:subsystem:deckhouse:manager):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
rbac.deckhouse.io/aggregate-to-deckhouse-as: manager
rbac.deckhouse.io/kind: custom-capability
rbac.deckhouse.io/scope: subsystem
rbac.deckhouse.io/subsystem: deckhouse
rbac.deckhouse.io/capability: "custom.subsystem-capability.deckhouse.superresource_view"
name: d8:custom:subsystem-capability:deckhouse:superresource:view
rules:
- apiGroups:
- mygroup.io
resources:
- mysuperresources
verbs:
- get
- list
- watch
Таким образом новая capability расширит роль d8:subsystem:deckhouse:manager.
Расширение подсистемных ролей с добавлением нового неймспейса
Если необходимо добавить новый неймспейс (для создания в нём хуком RoleBinding с namespace-ролью), потребуется добавить лишь один лейбл:
"rbac.deckhouse.io/namespace": namespace
Этот лейбл сообщает хуку, что в этом неймспейсе нужно создать RoleBinding с namespace-ролью:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
rbac.deckhouse.io/aggregate-to-deckhouse-as: manager
rbac.deckhouse.io/kind: custom-capability
rbac.deckhouse.io/scope: subsystem
rbac.deckhouse.io/subsystem: deckhouse
rbac.deckhouse.io/namespace: namespace
name: d8:custom:subsystem-capability:deckhouse:superresource:view
rules:
- apiGroups:
- mygroup.io
resources:
- mysuperresources
verbs:
- get
- list
- watch
Хук отслеживает ClusterRoleBinding и при создании привязки анализирует все системные и подсистемные роли, чтобы найти все объединенные в них capabilities с помощью проверки правила агрегации. Затем он берёт неймспейс из лейбла rbac.deckhouse.io/namespace и создает RoleBinding с namespace-ролью в этом неймспейсе.
Хук отслеживает только объекты с лейблом rbac.deckhouse.io/scope: system или subsystem. Capability без этого лейбла всё так же отдаёт свои правила роли через агрегацию, но её лейбл rbac.deckhouse.io/namespace не будет прочитан, и RoleBinding в неймспейсе не появится.
Расширение существующих namespace-ролей
Если ресурс принадлежит неймспейсу, необходимо расширить namespace-роль вместо системной/подсистемной. Разница лишь в лейблах и имени:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
rbac.deckhouse.io/aggregate-to-namespace-as: user
rbac.deckhouse.io/kind: custom-capability
rbac.deckhouse.io/scope: namespace
rbac.deckhouse.io/capability: "custom.namespace-capability.mycustom.superresource_view"
name: d8:custom:namespace-capability:mycustom:superresource:view
rules:
- apiGroups:
- mygroup.io
resources:
- mysuperresources
verbs:
- get
- list
- watch
Эта capability дополнит роль d8:namespace:user.
Создание собственной namespace- или проектной роли
Иногда встроенная иерархия уровней не подходит: например, нужна роль «разработчик» — просмотр всего неймспейса плюс чтение логов, но без права менять квоты или RBAC. Такая роль собирается из готовых capabilities, без написания RBAC-правил вручную.
Правила для собственных ролей:
- имя должно начинаться с
d8:custom:(например,d8:custom:namespace:developer); - роль должна иметь лейбл
rbac.deckhouse.io/kind: custom-role; - namespace- или проектная роль, которую будут выдавать через RoleBinding, должна также иметь
rbac.deckhouse.io/delegatable: "true". Каждый пользовательский неймспейс — это проект, и RoleBinding в нём принимают только роли с этим лейблом. На системные и подсистемные роли лейбл ставить нельзя — вебхук отклонит такую роль; - роль не может содержать собственных правил (
rules) — только агрегировать capabilities черезaggregationRule. Права описываются в отдельных capabilities — так состав роли всегда прозрачен; - нельзя в одной роли агрегировать capabilities пользовательских областей (
namespace,project) вместе с административными (system, подсистемы) — такая роль будет отклонена.
Пример: роль, включающая всё, что разрешено d8:namespace:viewer, плюс одну конкретную capability (подключение к подам), выбранную адресно по её уникальному лейблу rbac.deckhouse.io/capability:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: d8:custom:namespace:developer
labels:
rbac.deckhouse.io/kind: custom-role
rbac.deckhouse.io/scope: namespace
rbac.deckhouse.io/delegatable: "true" # Нужен, чтобы роль можно было указать в RoleBinding внутри проекта.
annotations:
custom.meta.deckhouse.io/title: "Разработчик"
custom.meta.deckhouse.io/description: "Просмотр ресурсов и подключение к подам, без управления квотами и RBAC"
aggregationRule:
clusterRoleSelectors:
# Всё, что входит в уровень viewer namespace-линейки.
- matchLabels:
rbac.deckhouse.io/aggregate-to-namespace-as: viewer
# Плюс одна конкретная capability, выбранная по её уникальному имени.
- matchLabels:
rbac.deckhouse.io/capability: "namespace-capability.kubernetes.access_terminal"
rules: []
Если готовой capability с нужными правами нет, создайте собственную (custom-capability может содержать правила) и добавьте в aggregationRule роли селектор по её лейблу rbac.deckhouse.io/capability (в примере ниже — matchLabels: {rbac.deckhouse.io/capability: "custom.logs-reader"}):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: d8:custom:namespace-capability:logs-reader
labels:
rbac.deckhouse.io/kind: custom-capability
rbac.deckhouse.io/capability: "custom.logs-reader"
rules:
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get", "list"]
Чтобы получить список всех доступных capabilities и их уникальных имён, используйте команду:
d8 k get clusterroles -l rbac.deckhouse.io/kind=capability \
-o custom-columns='NAME:.metadata.name,CAPABILITY:.metadata.labels.rbac\.deckhouse\.io/capability'
Созданная роль назначается так же, как и встроенная: через RoleBinding в неймспейсе или через ProjectRoleBinding на весь проект (для проектных ролей используйте rbac.deckhouse.io/scope: project и агрегируйте aggregate-to-project-as). Для RoleBinding в неймспейсе проекта на роли обязателен лейбл delegatable; ProjectRoleBinding его не требует (у него свой список допустимых префиксов). Назначить роль через ClusterRoleBinding нельзя — как и встроенные роли этих областей.
Собрать такую роль можно и без YAML — мастером выдачи доступа в веб-интерфейсе Deckhouse Console: он показывает доступные capabilities, собирает из них роль и сразу создаёт нужную привязку.
Как перевести кастомные роли на новую схему?
Вместе с переименованием ролей изменилась и схема лейблов, по которым агрегируются права.
У встроенных capabilities больше нет лейблов rbac.deckhouse.io/kind: manage и rbac.deckhouse.io/kind: use. Кастомная роль старой схемы, которая выбирает capabilities по этим лейблам, потеряла их права, а вместе с ней и её субъекты. Псевдонимы совместимости для кастомных ролей не создаются, поэтому почините такие роли, как описано ниже.
Селектор агрегации кастомной роли, который требует один из этих лейблов (например, rbac.deckhouse.io/kind: manage + rbac.deckhouse.io/aggregate-to-<SUBSYSTEM>-as), не находит ни одной встроенной capability.
По такому селектору контроллер агрегации Kubernetes берёт только правила кастомных capabilities, которые ещё несут старые лейблы.
Deckhouse создаёт объекты RoleBinding для субъектов роли с лейблом rbac.deckhouse.io/use-role в неймспейсах модулей, только если у роли есть лейбл rbac.deckhouse.io/scope: system или subsystem. Кастомная роль старой схемы с use-role и без такого лейбла потеряла эти объекты, даже если её селекторы уже переписаны.
Селектор, который требует лейбл агрегации линейки, которую не собирает ни одна встроенная роль, тоже не находит ни одной встроенной capability, потому что модули ставят на свои capabilities только лейблы линеек, которые собирают встроенные роли.
Это, например, лейбл rbac.deckhouse.io/aggregate-to-all-as старой схемы, лейбл подсистемы, которой больше нет в ролевой модели, или лейбл подсистемы, из которой ушёл модуль.
Исключение составляет линейка, которую несёт кастомная capability, потому что селектор по-прежнему получает правила этой capability. Кастомная capability здесь означает объект ClusterRole без лейбла heritage: deckhouse и без поля aggregationRule.
Исключение составляет и линейка собственной подсистемы роли из её лейбла rbac.deckhouse.io/subsystem, потому что её несут кастомные capabilities этой подсистемы, см. Расширение пользовательской роли.
Ни одно из исключений не относится к линейкам all, deckhouse, infrastructure, kubernetes и networking, которые не считаются кастомными.
Устаревшая роль-псевдоним, то есть ClusterRole с лейблом rbac.deckhouse.io/deprecated: "true", не считается встроенной ролью, которая собирает линейку, потому что линейку старой подсистемы она собирает только для привязок к старому имени.
Кастомная capability старой схемы тоже могла перейти в другие роли, см. Замена capability.
Такие роли перечисляет алерт D8UserAuthzLegacyRBACv2CustomRoleFound.
Он срабатывает на каждый объект ClusterRole без лейбла heritage: deckhouse и с полем aggregationRule, у которого есть селектор, требующий лейбл rbac.deckhouse.io/kind: manage или use, есть селектор, требующий такой лейбл агрегации, или есть лейбл rbac.deckhouse.io/use-role без rbac.deckhouse.io/scope: system или subsystem, независимо от имени роли.
Роль без поля aggregationRule, например замороженная ранее, в него не попадает.
Чтобы получить список таких ролей, используйте команду:
d8 k get clusterroles -o json | jq -r '
def lineages: [((.matchLabels // {}) | keys[]), (.matchExpressions[]? | select(.operator == "In" or .operator == "Exists") | .key)]
| map(capture("^rbac\\.deckhouse\\.io/aggregate-to-(?<lineage>.+)-as$").lineage);
([.items[] | select(.metadata.labels.heritage == "deckhouse" and .metadata.labels["rbac.deckhouse.io/deprecated"] != "true")
| .aggregationRule.clusterRoleSelectors[]? | lineages[]] + ["system", "namespace", "project"]) as $collected
| ([.items[] | select(.metadata.labels.heritage != "deckhouse" and .aggregationRule == null) | (.metadata.labels // {}) | keys[]
| capture("^rbac\\.deckhouse\\.io/aggregate-to-(?<lineage>.+)-as$").lineage]) as $carried
| .items[]
| select(.metadata.labels.heritage != "deckhouse" and .aggregationRule != null)
| (.metadata.labels["rbac.deckhouse.io/subsystem"] // "") as $own
| select(
(
((.metadata.labels["rbac.deckhouse.io/use-role"] // "") != "")
and (((.metadata.labels["rbac.deckhouse.io/scope"] // "") | IN("system", "subsystem")) | not)
)
or (
[
.aggregationRule.clusterRoleSelectors[]?
| (.matchLabels["rbac.deckhouse.io/kind"] // empty),
(.matchExpressions[]? | select(.key == "rbac.deckhouse.io/kind" and .operator == "In") | .values[])
]
| any(IN("manage", "use"))
)
or (
[
.aggregationRule.clusterRoleSelectors[]? | lineages[]
| select((IN($collected[]) | not)
and (IN("all", "deckhouse", "infrastructure", "kubernetes", "networking") or (. != $own and (IN($carried[]) | not))))
]
| length > 0
)
)
| .metadata.name'
Соответствие старой и новой схем:
| Было (старая схема) | Стало (новая схема) |
|---|---|
Произвольное имя роли (например, custom:manage:mycustom:manager) |
Обязательный префикс d8:custom: (например, d8:custom:mycustom:manager) |
rbac.deckhouse.io/kind: manage или use на кастомной роли |
rbac.deckhouse.io/kind: custom-role |
rbac.deckhouse.io/kind: manage или use на кастомной capability |
rbac.deckhouse.io/kind: custom-capability, имя с префиксом d8:custom: |
rbac.deckhouse.io/level: all \| subsystem \| module |
rbac.deckhouse.io/scope: system \| subsystem \| namespace |
rbac.deckhouse.io/kind: manage + rbac.deckhouse.io/aggregate-to-all-as: <уровень> на кастомной capability |
rbac.deckhouse.io/aggregate-to-system-as: <уровень> |
rbac.deckhouse.io/kind: use + rbac.deckhouse.io/aggregate-to-all-as: <уровень> на кастомной capability |
rbac.deckhouse.io/kind: custom-capability + rbac.deckhouse.io/scope: namespace + rbac.deckhouse.io/aggregate-to-namespace-as: <уровень>, имя d8:custom:namespace-capability:<имя> |
rbac.deckhouse.io/kind: manage + rbac.deckhouse.io/aggregate-to-all-as: <уровень> на кастомной роли |
ClusterRoleBinding роли на субъектов d8:manage:all:<уровень> и d8:system:<уровень>, потому что кастомная роль не может нести rbac.deckhouse.io/aggregate-to-system-as |
rbac.deckhouse.io/kind: use + rbac.deckhouse.io/aggregate-to-kubernetes-as: <уровень> на кастомной capability |
rbac.deckhouse.io/kind: custom-capability + rbac.deckhouse.io/scope: namespace + rbac.deckhouse.io/aggregate-to-namespace-as: <уровень>, имя d8:custom:namespace-capability:<имя> |
Селектор агрегации: rbac.deckhouse.io/kind: manage + rbac.deckhouse.io/aggregate-to-<подсистема>-as: <уровень> |
Только rbac.deckhouse.io/aggregate-to-<подсистема>-as: <уровень> |
Селектор для use-прав: rbac.deckhouse.io/kind: use + rbac.deckhouse.io/aggregate-to-kubernetes-as: <уровень> |
rbac.deckhouse.io/aggregate-to-namespace-as: <уровень> |
Селектор по модулю: rbac.deckhouse.io/kind: manage + module: <модуль> |
rbac.deckhouse.io/scope: system + module: <модуль> |
Имена встроенных capabilities также изменились (без псевдонимов):
d8:manage:permission:module:<модуль>:view|edit→d8:system-capability:<модуль>:view|edit;d8:use:capability:module:<модуль>:view|edit→d8:namespace-capability:<модуль>:view|edit.
Селекторы агрегации работают по лейблам, а не по именам. Прямые привязки к capabilities использовать не следует.
Починка роли
Почините каждую роль, которую перечисляет алерт D8UserAuthzLegacyRBACv2CustomRoleFound.
Соблюдайте порядок шагов, чтобы старая роль и её привязки оставались на месте, пока доступ не дадут новые.
-
Создайте рядом со старой ролью роль новой схемы с префиксом имени
d8:custom:, лейбломrbac.deckhouse.io/kind: custom-roleи селекторами агрегации новой схемы, безrbac.deckhouse.io/kind. См. пример «до и после». Селектор по лейблу агрегации линейки, которую не собирает ни одна встроенная роль, замените, как описано в разделе Селекторы по линейке, которую не собирает ни одна встроенная роль. Выберите область действия роли и назовите её соответственно:- Роль с лейблом
rbac.deckhouse.io/use-roleили с лейбломrbac.deckhouse.io/level: subsystemилиallполучает лейблыrbac.deckhouse.io/scope: subsystemиrbac.deckhouse.io/subsystem: <SUBSYSTEM>с именемd8:custom:<SUBSYSTEM>:<NAME>или лейблrbac.deckhouse.io/scope: systemс именемd8:custom:system:<NAME>. Сохраните на ней лейблrbac.deckhouse.io/use-role. Тогда для субъектов её объектов ClusterRoleBinding Deckhouse создаёт объекты RoleBinding на рольd8:namespace:*, которую называет этот лейбл, в неймспейсах модулей, системные capabilities которых агрегирует роль. Вебхукrbacv2-cluster-roles.deckhouse.ioотклоняет кастомную роль без лейблаrbac.deckhouse.io/scope, селекторы которой называют лейбл агрегации подсистемы или системной области, напримерrbac.deckhouse.io/aggregate-to-deckhouse-as. - Namespace- или проектная роль, которую вы выдаёте через RoleBinding, получает лейблы
rbac.deckhouse.io/scope: namespaceиrbac.deckhouse.io/delegatable: "true"с именемd8:custom:namespace:<NAME>. См. Создание собственной namespace- или проектной роли.
- Роль с лейблом
- Создайте capabilities новой схемы вместо кастомных capabilities старой схемы, которые агрегирует роль, как описано в разделе Замена capability. Старые capabilities пока не удаляйте.
- Для каждого объекта RoleBinding и ClusterRoleBinding, указывающего на старую роль, создайте объект с теми же субъектами, указывающий на новую роль. Поле
roleRefнеизменяемо, поэтому создайте новый объект, а не редактируйте старый. Если у старой роли есть лейблыrbac.deckhouse.io/kind: manageиrbac.deckhouse.io/aggregate-to-all-as, привяжите новую роль ещё и так, как описано в разделе Роли в составе системных ролей. Сrbac.deckhouse.io/kind: useона расширяла роли неймспейса, см. тот же раздел. -
Проверьте права субъектов:
d8 k auth can-i <VERB> <RESOURCE> --as=<USER_NAME> d8 k auth can-i <VERB> <RESOURCE> -n <NAMESPACE> --as=<USER_NAME>Чтобы проверить права группы, добавьте к
--asфлаг--as-group=<GROUP_NAME>. - Удалите старые объекты RoleBinding и ClusterRoleBinding, старую роль и старые capabilities.
Если объекты применяются из Git-репозитория (например, с помощью Argo CD или Flux), внесите те же изменения в репозиторий. Иначе следующее применение манифестов снова создаст старые объекты.
Роли в составе системных ролей
В старой схеме лейбл rbac.deckhouse.io/aggregate-to-all-as: <LEVEL> означал одно из двух, в зависимости от лейбла вида рядом с ним.
С rbac.deckhouse.io/kind: use он расширял роли d8:use:role:<LEVEL> во всех неймспейсах. Им соответствует d8:namespace:<LEVEL>, которая выбирает rbac.deckhouse.io/aggregate-to-namespace-as: <LEVEL>, поэтому перенесите такие права в capability неймспейса, как описано в разделе Замена capability.
С rbac.deckhouse.io/kind: manage роль с этим лейблом расширяла d8:manage:all:<LEVEL>, и дальше этот раздел о ней.
Ни одна встроенная роль этот лейбл больше не выбирает.
Псевдоним d8:manage:all:<LEVEL> и роль d8:system:<LEVEL> выбирают лейбл rbac.deckhouse.io/aggregate-to-system-as: <LEVEL>, а вебхук rbacv2-cluster-roles.deckhouse.io допускает этот лейбл только на кастомной capability.
Поэтому кастомная роль не может расширить эти роли, и субъекты, которые получали старую роль через них, получают новую роль только через собственный объект ClusterRoleBinding.
Следующие команды создают такой объект ClusterRoleBinding для каждого объекта ClusterRoleBinding на d8:manage:all:<LEVEL> и d8:system:<LEVEL>:
NEW_ROLE=<NEW_ROLE_NAME>
LEVEL=<LEVEL>
d8 k get clusterrolebindings -o json > clusterrolebindings.json
jq --arg role "$NEW_ROLE" --arg level "$LEVEL" '
{apiVersion: "v1", kind: "List", items: [.items[]
| select(.roleRef.kind == "ClusterRole" and (.roleRef.name | IN("d8:manage:all:" + $level, "d8:system:" + $level)))
| {apiVersion, kind, metadata: {name: ($role + ":" + .metadata.name)},
roleRef: {apiGroup: "rbac.authorization.k8s.io", kind: "ClusterRole", name: $role}, subjects}]}' \
clusterrolebindings.json > new-clusterrolebindings.json
d8 k apply -f new-clusterrolebindings.json
Перед созданием объектов просмотрите файл new-clusterrolebindings.json и удалите из него субъектов, которые не должны получить роль.
Deckhouse не синхронизирует эти объекты с объектами ClusterRoleBinding, из которых они скопированы.
Когда вы отзываете доступ субъекта, удалите оба. Имя копии заканчивается именем исходного объекта.
Селекторы по линейке, которую не собирает ни одна встроенная роль
Селектор по лейблу агрегации линейки выбирал capabilities, которые несли этот лейбл.
Например, rbac.deckhouse.io/aggregate-to-virtualization-as: <LEVEL> выбирал capabilities модуля virtualization.
Когда модуль ставит на них лейбл линейки подсистемы, в которой он теперь находится, селектор по этой линейке выбирает capabilities всех модулей подсистемы, а это может быть намного больше, чем давала роль раньше.
Чтобы новая роль осталась такой же узкой, как старая, выбирайте сами capabilities по лейблу rbac.deckhouse.io/capability.
Его значение не зависит от подсистемы. Для системной capability это system-capability.<MODULE_NAME>.<ACTION>, для capability неймспейса это namespace-capability.<MODULE_NAME>.<ACTION>.
-
Получите список capabilities модуля с уровнями ролей, которые их собирают:
d8 k get clusterroles -l rbac.deckhouse.io/kind=capability,module=<MODULE_NAME> -o json | jq -r ' .items[] | [.metadata.labels["rbac.deckhouse.io/capability"], (.metadata.labels | to_entries | map(select(.key | test("^rbac\\.deckhouse\\.io/aggregate-to-.+-as$")) | (.key | ltrimstr("rbac.deckhouse.io/aggregate-to-") | rtrimstr("-as")) + "=" + .value) | join(","))] | @tsv' - Оставьте capabilities, которые находил старый селектор. Селектор со значением
<LEVEL>находил capabilities этого уровня и, через встроенные роли нижних уровней, capabilities этих уровней. - В новой роли укажите по одному селектору по лейблу
rbac.deckhouse.io/capabilityдля каждой из них. Чтобы выдать все системные capabilities модуля независимо от уровня, достаточно одного селектора поrbac.deckhouse.io/scope: systemиmodule: <MODULE_NAME>.
Например, следующая роль неймспейса даёт в неймспейсе то же, что давал селектор по rbac.deckhouse.io/aggregate-to-virtualization-as: user, без других модулей подсистемы. Замените <ACTION> значениями из списка:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: d8:custom:namespace:virtualization-user
labels:
rbac.deckhouse.io/kind: custom-role
rbac.deckhouse.io/scope: namespace
rbac.deckhouse.io/delegatable: "true"
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.deckhouse.io/capability: namespace-capability.virtualization.<ACTION>
- matchLabels:
rbac.deckhouse.io/capability: namespace-capability.virtualization.<ACTION>
rules: []
Роль системной области или подсистемы выбирает значения system-capability.<MODULE_NAME>.<ACTION> так же.
Замена capability
Кастомная capability старой схемы несёт лейбл rbac.deckhouse.io/kind: manage или use и лейбл агрегации, и её называет алерт D8UserAuthzForeignAggregationLabel.
Пока на ней есть лейбл агрегации системной области, области неймспейса или проекта либо встроенной подсистемы, вебхук rbacv2-cluster-roles.deckhouse.io отклоняет любое её изменение.
Вебхук допускает такой лейбл только вместе с rbac.deckhouse.io/kind: custom-capability, а этот лейбл требует префикса имени d8:custom:, который существующий объект получить не может.
Создайте на её месте capability новой схемы с теми же правилами, а старую удалите, когда новая будет давать доступ.
Лейблы старой capability определяют новую:
| Старая capability | Новая capability |
|---|---|
rbac.deckhouse.io/kind: use + rbac.deckhouse.io/aggregate-to-kubernetes-as: <LEVEL> |
d8:custom:namespace-capability:<NAME> с лейблами rbac.deckhouse.io/scope: namespace и rbac.deckhouse.io/aggregate-to-namespace-as: <LEVEL> |
rbac.deckhouse.io/kind: manage + rbac.deckhouse.io/aggregate-to-<SUBSYSTEM>-as: <LEVEL> для встроенной или собственной подсистемы |
d8:custom:subsystem-capability:<NAME> с лейблами rbac.deckhouse.io/scope: subsystem, rbac.deckhouse.io/subsystem: <SUBSYSTEM> и тем же лейблом агрегации |
rbac.deckhouse.io/kind: use + rbac.deckhouse.io/aggregate-to-all-as: <LEVEL> |
d8:custom:namespace-capability:<NAME> с лейблами rbac.deckhouse.io/scope: namespace и rbac.deckhouse.io/aggregate-to-namespace-as: <LEVEL> |
rbac.deckhouse.io/kind: manage + rbac.deckhouse.io/aggregate-to-all-as: <LEVEL> |
d8:custom:system-capability:<NAME> с лейблами rbac.deckhouse.io/scope: system и rbac.deckhouse.io/aggregate-to-system-as: <LEVEL> |
Каждая новая capability также получает лейбл rbac.deckhouse.io/kind: custom-capability.
Старая capability с лейблом rbac.deckhouse.io/namespace: <NAMESPACE> давала субъектам агрегирующих её ролей объекты RoleBinding в этом неймспейсе, неймспейсе модуля. Deckhouse читает этот лейбл только на объектах ClusterRole с rbac.deckhouse.io/scope: system или subsystem, поэтому старая capability таких объектов RoleBinding больше не даёт. Перенесите лейбл на её замену, системную capability или capability подсистемы, и Deckhouse создаст их снова.
Capability с лейблами rbac.deckhouse.io/kind: use и rbac.deckhouse.io/aggregate-to-kubernetes-as: <LEVEL> расширяла роли d8:use:role:<LEVEL>.
Теперь она ничего к ним не добавляет, потому что d8:namespace:<LEVEL> и псевдонимы d8:use:role:<LEVEL> выбирают лейбл rbac.deckhouse.io/aggregate-to-namespace-as.
На уровнях viewer и manager её лейбл вместо этого выбирают роли d8:subsystem:kubernetes:<LEVEL>, какое бы значение ни было у её лейбла rbac.deckhouse.io/kind.
Через них её правила попадают в d8:system:<LEVEL> и в роли более высоких уровней, и их субъекты получают права этой capability во всех неймспейсах.
Замените такую capability в первую очередь. Например, следующие команды создают её аналог для namespace-ролей:
OLD_CAPABILITY=<OLD_CAPABILITY_NAME>
NEW_CAPABILITY=d8:custom:namespace-capability:<NAME>
LEVEL=<LEVEL>
d8 k get clusterrole "$OLD_CAPABILITY" -o json | jq --arg name "$NEW_CAPABILITY" --arg level "$LEVEL" '
{apiVersion, kind, metadata: {name: $name, labels: {
"rbac.deckhouse.io/kind": "custom-capability",
"rbac.deckhouse.io/scope": "namespace",
"rbac.deckhouse.io/aggregate-to-namespace-as": $level}},
rules}' > new-capability.json
d8 k apply -f new-capability.json
Проверьте права субъектов namespace-ролей, а затем удалите старую capability:
d8 k delete clusterrole "$OLD_CAPABILITY"
После удаления её права пропадают и из ролей d8:subsystem:kubernetes:<LEVEL>.
Замороженные роли
Роль, которую заморозили до того, как встроенные capabilities получили новые лейблы (удалили поле aggregationRule и сохранили правила), сохраняет свои права, и алерт D8UserAuthzLegacyRBACv2CustomRoleFound её не перечисляет.
-
Явные объекты RoleBinding, которые заменили её автоматические, указывают на
d8:use:role:<LEVEL>. Новая схема сохраняет эти роли только как устаревшие псевдонимыd8:namespace:<LEVEL>. Такие объекты называет алертD8UserAuthzDeprecatedRBACv2RoleInUse. Переведите их наd8:namespace:<LEVEL>, потому что привязка к псевдониму перестаёт давать доступ, когда псевдоним удаляется. Следующие команды делают это для объектов RoleBinding с префиксом имениexplicit::d8 k get rolebindings -A -o json | jq '{apiVersion: "v1", kind: "List", items: [.items[] | select(.roleRef.kind == "ClusterRole" and (.roleRef.name | startswith("d8:use:role:")) and (.metadata.name | startswith("explicit:")))]}' \ > use-role-rolebindings.json jq '.items |= map({apiVersion, kind, metadata: {name: ("namespace:" + .metadata.name), namespace: .metadata.namespace, annotations: ((.metadata.annotations // {}) | with_entries(select(.key == "rbac.deckhouse.io/related-with")))}, roleRef: (.roleRef | .name |= (sub("^d8:use:role:"; "d8:namespace:") | sub(":kubernetes$"; ""))), subjects})' \ use-role-rolebindings.json > namespace-rolebindings.json d8 k apply -f namespace-rolebindings.json && d8 k get -f namespace-rolebindings.json > /dev/null && d8 k delete -f use-role-rolebindings.jsonСтарые объекты удаляются, только если все новые уже есть, и после сбоя команды можно запустить снова.
-
Если на роли остался лейбл
rbac.deckhouse.io/aggregate-to-all-as, её называет алертD8UserAuthzForeignAggregationLabel. Ни одна встроенная роль этот лейбл больше не выбирает, поэтому удалите его:d8 k label clusterrole <ROLE_NAME> rbac.deckhouse.io/aggregate-to-all-as- -
Замороженная роль не получает права на ресурсы, добавленные позже, например на ресурсы нового модуля. Чтобы они появились, замените её ролью новой схемы, как описано в разделе Починка роли.
Примеры
Кастомная роль до и после
Пример конфигурации роли, объединяющей права подсистем deckhouse и kubernetes и модуля user-authn.
-
Было (старая схема):
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: custom:manage:mycustom:manager labels: rbac.deckhouse.io/use-role: admin rbac.deckhouse.io/kind: manage rbac.deckhouse.io/level: subsystem rbac.deckhouse.io/subsystem: custom rbac.deckhouse.io/aggregate-to-all-as: manager aggregationRule: clusterRoleSelectors: - matchLabels: rbac.deckhouse.io/kind: manage rbac.deckhouse.io/aggregate-to-deckhouse-as: manager - matchLabels: rbac.deckhouse.io/kind: manage rbac.deckhouse.io/aggregate-to-kubernetes-as: manager - matchLabels: rbac.deckhouse.io/kind: manage module: user-authn rules: [] -
Стало (новая схема):
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: d8:custom:mycustom:manager labels: rbac.deckhouse.io/use-role: admin rbac.deckhouse.io/kind: custom-role rbac.deckhouse.io/scope: subsystem rbac.deckhouse.io/subsystem: mycustom aggregationRule: clusterRoleSelectors: - matchLabels: rbac.deckhouse.io/aggregate-to-deckhouse-as: manager - matchLabels: rbac.deckhouse.io/aggregate-to-kubernetes-as: manager - matchLabels: rbac.deckhouse.io/scope: system module: user-authn rules: []
Что изменилось:
- имя получило обязательный префикс
d8:custom:и называет подсистему роли; rbac.deckhouse.io/kind: manage→rbac.deckhouse.io/kind: custom-role;rbac.deckhouse.io/level: subsystem→rbac.deckhouse.io/scope: subsystem;- лейбл
rbac.deckhouse.io/aggregate-to-all-asудалён, субъектыd8:system:managerполучают роль через ClusterRoleBinding, см. Роли в составе системных ролей; - из селекторов агрегации убран лейбл
rbac.deckhouse.io/kind: manage; - выборка всех системных прав модуля теперь выполняется по
rbac.deckhouse.io/scope: system+module: <модуль>.
Кастомная capability до и после
Пример конфигурации capability, которая даёт права на просмотр ресурса MySuperResource и агрегируется в роль из примера выше (в её поле aggregationRule должен быть селектор rbac.deckhouse.io/aggregate-to-mycustom-as: manager).
-
Было (старая схема):
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: custom:manage:permission:mycustom:superresource:view labels: rbac.deckhouse.io/kind: manage rbac.deckhouse.io/aggregate-to-mycustom-as: manager rules: - apiGroups: - mygroup.io resources: - mysuperresources verbs: - get - list - watch -
Стало (новая схема):
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: d8:custom:subsystem-capability:mycustom:superresource:view labels: rbac.deckhouse.io/kind: custom-capability rbac.deckhouse.io/scope: subsystem rbac.deckhouse.io/subsystem: mycustom rbac.deckhouse.io/aggregate-to-mycustom-as: manager rules: - apiGroups: - mygroup.io resources: - mysuperresources verbs: - get - list - watch
Лейблы и аннотации: было и стало
Лейблы на объектах ClusterRole:
| Лейбл | Было | Стало | Назначение |
|---|---|---|---|
rbac.deckhouse.io/kind |
manage или use |
custom-role / custom-capability для кастомных объектов; role / capability у встроенных (зарезервированы) |
Тип объекта ролевой модели. Обязателен, объекты без него не обрабатываются |
rbac.deckhouse.io/level |
all | subsystem | module |
Удалён | Старый уровень роли; заменён лейблом scope |
rbac.deckhouse.io/scope |
— | system | subsystem | namespace |
Область действия роли или capability |
rbac.deckhouse.io/subsystem |
Имя подсистемы | Без изменений | Подсистема роли; используется при scope: subsystem |
rbac.deckhouse.io/use-role |
Уровень use-роли | Уровень namespace-роли | Какая namespace-роль автоматически выдаётся обладателю системной/подсистемной роли в системных неймспейсах её модулей (через автоматически создаваемые объекты RoleBinding) |
rbac.deckhouse.io/aggregate-to-all-as |
<уровень> |
С rbac.deckhouse.io/kind: manage заменён лейблом rbac.deckhouse.io/aggregate-to-system-as на кастомной capability, который кастомная роль нести не может. С rbac.deckhouse.io/kind: use заменён лейблом rbac.deckhouse.io/aggregate-to-namespace-as на кастомной capability |
Агрегация объекта в общесистемную роль (d8:system:<уровень>) |
rbac.deckhouse.io/aggregate-to-<подсистема>-as |
Использовался в селекторах вместе с rbac.deckhouse.io/kind: manage |
Используется в селекторах сам по себе | Агрегация объекта в подсистемную роль указанного уровня |
rbac.deckhouse.io/aggregate-to-kubernetes-as |
<уровень> (для use-прав) |
По-прежнему для подсистемы kubernetes (d8:subsystem:kubernetes:*). Старый use-смысл переехал на aggregate-to-namespace-as |
Агрегация в d8:subsystem:kubernetes:<уровень> |
rbac.deckhouse.io/namespace |
Неймспейс | Без изменений | Дополнительный неймспейс, в котором обладателям роли автоматически создаётся RoleBinding |
rbac.deckhouse.io/capability |
— | Уникальное имя capability (например, system-capability.deckhouse.view) |
Машиночитаемый идентификатор встроенной capability |
rbac.deckhouse.io/deprecated |
— | "true" на ролях-псевдонимах |
Роль устарела и будет удалена; переведите привязки на новую роль |
module |
Имя модуля | Без изменений | Принадлежность встроенного объекта модулю DP; удобен в селекторах агрегации вместе со scope |
heritage: deckhouse |
Признак объекта платформы | Без изменений | Устанавливать на кастомные объекты нельзя |
Аннотации на объектах ClusterRole (в старой схеме аннотации не использовались):
| Аннотация | Назначение |
|---|---|
ru.meta.deckhouse.io/title, ru.meta.deckhouse.io/description |
Отображаемые название и описание роли или capability на русском языке (платформа ставит их на встроенные объекты; на кастомных можно указать свои) |
en.meta.deckhouse.io/title, en.meta.deckhouse.io/description |
То же на английском языке |
rbac.deckhouse.io/deprecated-replaced-by |
Стоит на ролях-псевдонимах прежних имён и называет новую роль, на которую нужно перевести привязки. Псевдоним агрегирует capabilities новой роли, поэтому существующие привязки к прежнему имени дают доступ, пока псевдонимы не удалены. Это не те же права, что прежде, потому что d8:use:role:admin больше не даёт выпуск токена ServiceAccount и impersonate |
Добавление кастомной capability (в новой схеме)
Capability — это обычный объект ClusterRole с правилами, который через лейбл агрегации автоматически включается в выбранную роль. В новой схеме кастомная capability создаётся следующим образом:
- Определите, какую роль нужно расширить: namespace-роль, подсистемную, системную или кастомную.
- Создайте ClusterRole с префиксом имени
d8:custom:(для читаемости —d8:custom:capability:<имя>:<ресурс>:<действие>), лейбломrbac.deckhouse.io/kind: custom-capabilityи лейблом агрегации целевой роли:rbac.deckhouse.io/aggregate-to-namespace-as: <viewer|user|manager|admin|superadmin>— в namespace-рольd8:namespace:<уровень>;rbac.deckhouse.io/aggregate-to-<подсистема>-as: <viewer|manager|superadmin>— в подсистемную рольd8:subsystem:<подсистема>:<уровень>;rbac.deckhouse.io/aggregate-to-system-as: <viewer|manager|superadmin>— в системную рольd8:system:<уровень>;rbac.deckhouse.io/aggregate-to-<имя своей подсистемы>-as: <уровень>— в кастомную роль (такой селектор должен присутствовать в её полеaggregationRule).
- Опишите права в
rules.
Kubernetes агрегирует правила автоматически: сразу после создания capability её права появятся у всех обладателей целевой роли. Проверить результат можно командой d8 k auth can-i --as <пользователь> или посмотрев итоговые правила роли: d8 k get clusterrole <роль> -o yaml.
Примеры конфигурации доступны выше в подразделах «Кастомная роль до и после» и «Кастомная capability до и после».
Как переименовать встроенную роль?
Изменять права встроенных ролей нельзя, но можно изменить их отображаемое название и описание — например, чтобы в интерфейсе они назывались в принятых в компании терминах. Для этого добавьте на роль аннотации custom.meta.deckhouse.io/title и custom.meta.deckhouse.io/description:
d8 k annotate clusterrole d8:namespace:admin \
custom.meta.deckhouse.io/title='Администратор команды' \
custom.meta.deckhouse.io/description='Управление ресурсами и доступом в пространстве имён команды'
Это единственное изменение, которое разрешено вносить в объекты с префиксом d8: (кроме d8:custom:*): попытка изменить правила, агрегацию или лейблы встроенной роли будет отклонена.
Как узнать, у кого есть доступ к ресурсу?
При включённом режиме мультитенантности (enableMultiTenancy) доступен обратный запрос к авторизации — ресурс WhoCan. Он отвечает на вопрос «кто может выполнить действие X над ресурсом Y?» и возвращает список пользователей, групп и ServiceAccount’ов:
d8 k create -o yaml -f - <<EOF
apiVersion: authorization.deckhouse.io/v1alpha1
kind: WhoCan
metadata:
name: who-can-create-networkpolicies
spec:
resourceAttributes:
namespace: my-namespace
verb: create
group: networking.k8s.io
resource: networkpolicies
EOF
Ответ возвращается в поле status (users, groups, serviceAccounts) сразу в выводе команды; объект нигде не сохраняется.
Право создавать WhoCan-запросы даёт кластерная роль d8:user-authz:who-can-checker. Она никому не выдана по умолчанию: результат запроса раскрывает субъекты доступа во всех неймспейсах, поэтому выдавайте её только доверенным администраторам через ClusterRoleBinding.
Как узнать, что разрешено конкретному пользователю, группе или ServiceAccount’у?
При включённом режиме мультитенантности (enableMultiTenancy) доступен ресурс SubjectAccessReport — обратная сторона WhoCan. Он отвечает на вопрос «что разрешено этому субъекту» и сразу возвращает отчёт: какие роли и через какие привязки выданы, какие действия и над какими ресурсами разрешены в кластере и в каждом неймспейсе, и откуда взялось каждое право.
d8 k create -o yaml -f - <<EOF
apiVersion: authorization.deckhouse.io/v1alpha1
kind: SubjectAccessReport
metadata:
name: what-can-jane-do
spec:
subject:
kind: User
name: jane@example.com
EOF
Поле spec.subject.kind принимает значения User, Group и ServiceAccount (для последнего обязательно укажите spec.subject.namespace). Если spec.subject не указан, отчёт строится для того, кто выполняет запрос.
Особенности отчёта:
- Группы пользователя определяются автоматически по каталогу Group, включая вложенные: если пользователь состоит в группе
B, аBвходит вA, праваAтоже попадут в отчёт. Учтённые группы возвращаются вstatus.subject.groups. Группы, которых нет в каталоге (например, приходящие из внешнего провайдера аутентификации), можно передать вspec.groups. - В каждом источнике права (
status.scopes[].resources[].sources[]) есть полеmatchedBy: по нему видно, выдано право лично субъекту или через группу — и можно посмотреть картину прав без учёта групп. - Неймспейсы с одинаковым доступом объединяются в одну секцию (
status.scopes[].namespaces), поэтому проект с десятком неймспейсов не превращается в десяток одинаковых таблиц. - Права, выданные ClusterRoleBinding, показываются один раз в кластерной области (
status.scopes[].cluster: true) — они действуют во всех неймспейсах. - Ограничить отчёт конкретными неймспейсами можно через
spec.namespaces.
Отчёт строится по данным RBAC. Ограничения admission-вебхуков не сворачиваются в строки RBAC — они помечаются в status.scopes[].caveat. Например, изменение и удаление системных ресурсов запрещено ниже уровня superadmin — кроме администраторов кластера (cluster-admin, user-authz:super-admin и bypass-групп). Если права субъекта ограничены ресурсом ClusterAuthorizationRule, об этом сообщается в status.notes.
Право строить отчёт о другом субъекте даёт кластерная роль d8:user-authz:subject-access-checker. Как и who-can-checker, она намеренно никому не выдана по умолчанию: отчёт раскрывает полную карту прав субъекта, включая чужие неймспейсы. Отчёт о самом себе доступен любому аутентифицированному пользователю и не требует дополнительных прав.
Как пользователю увидеть список доступных ему неймспейсов?
При включённом режиме мультитенантности (enableMultiTenancy) список неймспейсов фильтруется автоматически: команда d8 k get namespaces возвращает пользователю только те неймспейсы, к которым у него есть доступ — независимо от механизма предоставления доступа (привязки ролей, ProjectRoleBinding/ClusterProjectRoleBinding, ClusterAuthorizationRule/AuthorizationRule). Пользователь не видит чужих неймспейсов и не может по списку узнать об их существовании.
Тот же список отдаёт read-only ресурс accessiblenamespaces — его может запросить любой аутентифицированный пользователь для самого себя:
d8 k get accessiblenamespaces
Это удобно для скриптов и интерфейсов: не нужно перебирать неймспейсы и проверять доступ к каждому.