Kubernetes RBAC
RBAC в Kubernetes — это deny-by-default, но грабли в выдачах: wildcard-глагол, право создавать поды или привязка cluster-admin тихо равны захвату кластера. Наименьшие привилегии здесь — дисциплина по каждому глаголу, а не галочка.
Коллега в три часа ночи отлаживает капризный деплой, и самый быстрый способ убрать ошибку — однострочник, который хоть раз набирал каждый: kubectl create clusterrolebinding ci-debug --clusterrole=cluster-admin --serviceaccount=ci:runner. Пайплайн зеленеет, тикет закрывается, никто это не откатывает. Через полгода под этого CI-раннера пробивают через несвязанный баг в приложении — и поскольку токен его service-account примонтирован в /var/run/secrets/kubernetes.io/serviceaccount/token и привязан к cluster-admin, атакующий получает не один под. Он получает каждый секрет в каждом неймспейсе, возможность запланировать привилегированный под на любой ноде и путь к самому хосту. RBAC всё это время был включён. Он сказал «да», потому что кто-то ему так велел.
К концу урока ты сможешь прочитать пару Role/RoleBinding и точно сказать, что она выдаёт, распознать три привязки, которые тихо равны захвату кластера, и спроектировать service-account, который делает свою работу и ничего сверх того.
Четыре существительных, и почему два из них — ловушка
RBAC в Kubernetes собран ровно из четырёх видов объектов, и вся модель безопасности живёт в том, как ты их комбинируешь.
- Role — это неймспейс-ограниченный список правил: кортежи
apiGroups,resourcesиverbs(get,list,watch,create,update,patch,delete). Role не выдаёт ничего, пока не привязана. - ClusterRole — та же форма, но в области всего кластера: она может называть кластерные ресурсы (nodes, persistentvolumes) и переиспользоваться между неймспейсами.
- RoleBinding прикрепляет Role (или ClusterRole) к субъекту внутри одного неймспейса.
- ClusterRoleBinding прикрепляет ClusterRole к субъекту по всему кластеру.
Субъект — обычно ServiceAccount, та идентичность, под которой работает каждый под. Вот что упускают: по умолчанию каждый под получает токен service-account, примонтированный в его файловую систему, и этот токен и есть учётка пода к API-серверу. Поэтому радиус поражения любой выдачи RBAC — это «всё, что атакующий сможет запустить в поде, использующем этот service-account». RBAC действительно работает по принципу deny-by-default: свежий service-account не может практически ничего. Но опасность не в дефолте. Она в правах, которые ты добавляешь, потому что две комбинации превращают мелкую ошибку в полную компрометацию: ClusterRoleBinding (на весь кластер, а не на один неймспейс) и встроенная ClusterRole cluster-admin (каждый глагол на каждом ресурсе, включая *).
Грабли cluster-admin, по порядку
Причина, по которой инциденты с RBAC выглядят одинаково в разных компаниях, — повторяются одни и те же три паттерна. Вот что каждый из них на самом деле выдаёт, почему он соблазнителен и какая безопасная замена.
| Грабли | Что реально выдаёт | Почему случается | Фикс по наименьшим привилегиям |
|---|---|---|---|
| привязка cluster-admin | Каждый глагол на каждом ресурсе, все неймспейсы — полный захват | Отладка «убрать ошибку»; скопированные значения Helm | Узкая Role с 3-4 реально используемыми глаголами |
wildcard глагол/ресурс (*) | Сегодняшние ресурсы и каждый будущий, что добавит оператор | «Пока не знаю точные глаголы, ужму позже» | Перечисли глаголы явно; не отгружай verbs: [”*“] |
create pods / pods/exec | Запланировать привилегированный под или войти exec — пивот к хосту/другим SA | Выглядит как обычный глагол приложения; ревьюеры пропускают | Считай create pods привилегированным; закрой Pod Security + политикой |
get/list secrets | Читать каждый секрет в области, включая токены других SA | «Приложению нужен один секрет» → выдали на весь глагол | Ограничь через resourceNames; предпочти projected/CSI-секреты |
escalate / bind на ролях | Выдать себе права, которых нет — повышение привилегий | Спрятано в ClusterRole оператора, которую никто не аудит | Никогда не выдавай escalate/bind рабочим нагрузкам |
Сквозная мысль: в RBAC нет правила deny. Нельзя «вычесть» право задним числом — субъект может делать то, что даёт объединение всех привязанных правил, и если хоть одно говорит «да», то это «да». Именно поэтому избыточная выдача так опасна: нет страховочного правила, которое тебя поймает, поэтому наименьшие привилегии приходится обеспечивать в момент записи, глагол за глаголом. Поведение агрегации усугубляет это: ClusterRole с меткой rbac.authorization.k8s.io/aggregate-to-edit: "true" автоматически вливаются во встроенную роль edit, так что оператор кастомного ресурса может тихо расширить то, что могут делать пользователи с привязкой edit, не трогая твои привязки.
▸Частая ошибка
Самое частое реальное повышение привилегий — не сам cluster-admin, а create pods в сочетании с отсутствием ограничений Pod Security. Если service-account может создать под, атакующий, владеющий этим SA, может запланировать под с примонтированным hostPath: / или hostPID: true, считать учётки kubelet с ноды и пивотнуть к каждой рабочей нагрузке на этой ноде — включая те, у которых токены куда мощнее. Поэтому «может ли этот SA создавать поды?» — самый сигнальный вопрос в ревью RBAC, и поэтому create pods обязан идти в паре с профилем Pod Security Admission restricted, чтобы вообще что-то значить.
Проектирование service-account по наименьшим привилегиям
Зрелый рефлекс — строить выдачи снизу вверх от наблюдаемой потребности, а не сверху вниз от удобства. Порядок работы:
- Старт от deny. У нового ServiceAccount нет прав. Сопротивляйся желанию привязать широкую встроенную роль «чтобы разблокироваться».
- Перечисли реальные глаголы. Запусти нагрузку, зафиксируй, какие вызовы API она делает (аудит-логи или
kubectl auth can-i --list), и выдай ровно их —get/list/watchна конкретных ресурсах, без wildcard. - Предпочитай Role вместо ClusterRole, RoleBinding вместо ClusterRoleBinding. Держи выдачу в одном неймспейсе, если ресурс не является истинно кластерным. Неймспейс-выдача ограничивает радиус поражения одним неймспейсом.
- Ограничивай секреты через
resourceNames. Если приложению нужен один секрет — назови его; не выдавай весь глаголsecrets. - Выключай монтаж токена, когда он не нужен. Поставь
automountServiceAccountToken: falseна подах, которые никогда не зовут API-сервер — большинство подов приложений не зовёт, а непримонтированный токен нельзя украсть. - Аудит через
kubectl auth can-i. Перед отгрузкой спроси кластер напрямую:kubectl auth can-i create pods --as=system:serviceaccount:app:web -n appдолжно вернутьno.
Бэкенд-поду для старта нужно прочитать один ConfigMap и один Secret в своём неймспейсе. CI красный, потому что SA не может их прочитать. Выбери верную выдачу.
Role выдаёт `verbs: [list]` на `secrets` в одном неймспейсе, привязана к ServiceAccount пода. Под скомпрометирован. Что атакующий сделает с примонтированным токеном?
Почему `create pods` — настолько сигнальное право для пометки в ревью RBAC, даже без явной привязки cluster-admin?
Упорядочь шаги проектирования service-account по наименьшим привилегиям, от первого к последнему:
- 1 Старт от deny — свежий ServiceAccount без привязок
- 2 Перечисли точные глаголы, которые нагрузка реально вызывает
- 3 Выдай неймспейс-ограниченную Role (предпочти Role вместо ClusterRole), ограниченную resourceNames
- 4 Привяжи её через RoleBinding к этому одному ServiceAccount
- 5 Проверь через `kubectl auth can-i`, что ничего лишнего не разрешено
- 01Пройди шаг за шагом, как одна привязка ClusterRoleBinding к cluster-admin на CI service-account превращается в полную компрометацию кластера при пробое одного пода.
- 02Почему `list secrets` опаснее, чем кажется, и какая безопасная альтернатива по наименьшим привилегиям?
RBAC в Kubernetes собран из четырёх объектов — Role и ClusterRole (правила глагол/ресурс) плюс RoleBinding и ClusterRoleBinding (которые прикрепляют их к субъекту, обычно ServiceAccount). Модель действительно deny-by-default и только разрешающая: свежий SA не может ничего, а объединение всех привязанных правил — это то, что может его авто-примонтированный токен, и нет deny-правила, чтобы отозвать выдачу. Поэтому грабли — это всегда переизбыточные выдачи, а не дефолты: привязка cluster-admin (каждый глагол везде), wildcard-глагол, покрывающий и будущие ресурсы, create pods (запланировать привилегированный под, пивотнуть к ноде), list secrets (прочитать каждый секрет, включая чужие токены) и escalate/bind (выдать себе больше). Наименьшие привилегии — дисциплина момента записи: старт от deny, перечисли реальные глаголы, предпочти неймспейс-ограниченные Role + RoleBinding, ограничь секреты через resourceNames, выключи монтаж токена, когда он не нужен, и проверь через kubectl auth can-i. Теперь, видя привязку в ревью, твой первый вопрос: кто субъект, кластерная ли она и может ли создавать поды или читать секреты?
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.