RBAC и мультиарендность в Kubernetes
Завёл в кластере отдельную учётную запись для сервиса и выдал ей право только читать поды. Проверил четырьмя вопросами: читать поды - можно; удалять поды - нельзя; читать секреты - нельзя; создавать развёртывания - нельзя. Права описываются явно, и всё, что не разрешено, запрещено по умолчанию.
Стержень: доступ строится из трёх частей - кто, что можно и где; всё, что не выдано явно, запрещено.
// Формулировки: «как устроен RBAC (role-based access control), доступ по ролям?», «чем Role отличается от ClusterRole?», «какие права давать сервису?»
Три части и связка между ними
Первая часть - субъект: кто действует. Живой человек, группа людей или учётная запись сервиса, от имени которой работают поды. Вторая - роль: список разрешённых действий над видами объектов. Третья - привязка, которая соединяет первое со вторым.
Область действия задаётся уровнем. Роль отсека кластера действует только в своём отсеке: читать поды в отсеке платежей и больше нигде. Роль уровня кластера действует везде и нужна для вещей, у которых нет отсека, - узлов, самих пространств, определений типов объектов.
// Проверять права надо не глазами по описанию, а вопросом к кластеру: «может ли вот этот субъект сделать вот это». Я так и делал, и получил четыре однозначных ответа. Эта же команда - лучший способ разобраться, почему что-то не работает: вместо чтения ролей вы просто спрашиваете и получаете да или нет.
- субъект
- кто действует: человек, группа или учётная запись сервиса
- роль
- список разрешённых действий над видами объектов
- привязка
- соединяет субъект с ролью; без неё роль ничего не даёт
Минимальные права на практике
Начинают с нуля и добавляют по факту отказа, а не наоборот. Практический порядок: выдать пусто, запустить, посмотреть, на чём споткнулось, добавить ровно это. Дольше, чем выдать всё сразу, зато итог получается честным.
Типовые ошибки видны сразу. Учётная запись по умолчанию, от которой работают все поды отсека, - в неё нельзя добавлять права, иначе их получают все. Роль администратора кластера, выданная сервису «чтобы не разбираться». Право читать секреты во всём кластере там, где нужен один конкретный секрет.
// Отдельная вещь, которую забывают: по умолчанию под получает подключённый токен своей учётной записи, и приложение может обращаться к точке входа кластера. Если приложению это не нужно, подключение токена отключают явно - одна строка в описании пода, которая убирает целый класс возможностей у того, кто попадёт внутрь контейнера.
- учётная запись сервиса
- личность, от имени которой работают поды
- подключённый токен
- пропуск к точке входа кластера внутри пода; отключается одной строкой
Границы отсеков и разделение
Отсек кластера - естественная граница прав. Команде выдают роли в её отсеке, и туда же вешают квоты на ресурсы и пределы по умолчанию. Это самый дешёвый способ развести команды: они не видят и не трогают чужое.
Но граница эта неполная. Узлы, тома, определения типов объектов живут вне отсеков; сеть по умолчанию плоская, и под из одного отсека может обратиться к сервису в другом, если не настроены сетевые правила. Разделение прав не даёт сетевого разделения.
// Для настоящего разделения к правам добавляют сетевые правила (кто с кем может общаться) и ограничения на то, какие поды вообще разрешено запускать - от запуска от root до проброса каталогов узла. Права отвечают на вопрос «кто что может менять», остальное - на вопрос «что может сам запущенный под».
- отсек кластера
- граница для прав, квот и умолчаний; но не для сети
- сетевые правила
- кто с кем может общаться внутри кластера; настраиваются отдельно
Как отвечать: «Как выдать сервису доступ к кластеру правильно?»
По минимуму и явно. Завожу отдельную учётную запись именно для этого сервиса - не использую общую по умолчанию, потому что права на неё получат все поды отсека. Дальше описываю роль со списком конкретных действий над конкретными видами объектов и привязываю её к этой записи. Область беру самую узкую: роль в одном отсеке, а не роль уровня кластера, если только речь не про узлы или другие объекты без отсека. Проверяю не чтением описаний, а прямым вопросом к кластеру «может ли этот субъект сделать это» - я так и делал на стенде и получил однозначные ответы: читать поды можно, удалять нельзя, секреты нельзя. И отдельно смотрю, нужен ли поду вообще доступ к точке входа кластера: если нет, отключаю подключение токена одной строкой в описании.
Ответ описывает всю конструкцию, выбирает минимальную область и заканчивается настройкой, которую почти всегда забывают. Проверка вопросом вместо чтения - признак практики.
На чём валятся
- −Добавляют права учётной записи по умолчанию: их получают все поды отсека.
- −Выдают роль администратора кластера сервису, чтобы не разбираться.
- −Берут роль уровня кластера там, где хватило бы роли в одном отсеке.
- −Оставляют подключённый токен там, где приложению кластер не нужен.
- −Считают, что разделение прав даёт и сетевое разделение. Не даёт.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 7, остальные разбираются в тренажёре.
- Приложению не нужен API кластера, но токен сервисного аккаунта монтируется в под. Чем это плохо?A)Токен занимает место в памяти пода и мешает лимитамB)Пробитое приложение получает готовый доступ к API кластераC)Kubelet будет считать под системным и не станет его вытеснятьD)Под не сможет обращаться к внешним сервисам без прокси
показать ответ и разбор
+B)Пробитое приложение получает готовый доступ к API кластера// разбор: Токен по умолчанию лежит в файловой системе контейнера, и любой, кто выполнил в нём код, получает права соответствующего сервисного аккаунта. Даже минимальные права дают разведку кластера, а щедрые — доступ к секретам соседей. Потому монтирование отключают через automountServiceAccountToken: false там, где API не нужен, а нужным подам заводят отдельный аккаунт с точечными правами.
- Команды разнесли по namespace и считают, что изолировали друг от друга. Чего namespace не даёт?A)Разделения имён объектов внутри кластераB)Точки применения прав RBAC на группу объектовC)Области для квот на ресурсы и лимитов по умолчаниюD)Сетевой изоляции и разделения ядра нод между подами
показать ответ и разбор
+D)Сетевой изоляции и разделения ядра нод между подами// разбор: Namespace — это область имён и точка приложения прав и квот, но не граница безопасности. По сети поды разных namespace общаются свободно, пока не заданы NetworkPolicy; ядро у нод общее, поэтому побег из контейнера цепляет соседей; кластерные объекты и CRD видны всем. Настоящая многоарендность строится связкой из политик, квот, admission-правил и разнесения по нодам или кластерам.
- Что такое ServiceAccount и как под получает права к API кластера?A)Это учётка человека-разработчика для kubectlB)Аккаунт ноды, под наследует её права автоматическиC)Идентичность пода в API, права даёт RoleBindingD)Токен реестра образов для скачивания контейнеров
показать ответ и разбор
+C)Идентичность пода в API, права даёт RoleBinding// разбор: ServiceAccount — идентичность рабочей нагрузки внутри кластера: под запускается под каким-то SA (если не указан — default в своём namespace), и в него монтируется токен для обращения к API. Сами по себе права SA не даёт — их выдаёт RoleBinding/ClusterRoleBinding, связывающий SA с ролью. Разделение: SA отвечает на «кто», роль и привязка — на «что разрешено».
- Есть готовая ClusterRole «читать поды». Нужно дать её приложению только в одном namespace. Как?A)Создать ClusterRoleBinding — иначе ClusterRole не действуетB)Скопировать ClusterRole в Role внутри namespaceC)Навесить на под label с именем namespaceD)Привязать ClusterRole через RoleBinding в этом namespace
показать ответ и разбор
+D)Привязать ClusterRole через RoleBinding в этом namespace// разбор: ClusterRole можно привязать не только глобально (ClusterRoleBinding), но и точечно: RoleBinding в конкретном namespace даёт права этой ClusterRole только в нём. Так переиспользуют общий набор прав (например, «читать поды») по разным namespace без копирования в отдельные Role. ClusterRoleBinding же раздал бы права во всём кластере — избыточно и опасно.
- Приложению «чтобы просто заработало» выдали cluster-admin. Чем это грозит и как исправить?A)Ничего: под всё равно ограничен своим namespaceB)Пробитый под получает весь кластер; сузить до нужногоC)Достаточно перевесить cluster-admin на ноду, а не на подD)Проблема только в аудите, сами права безопасны
показать ответ и разбор
+B)Пробитый под получает весь кластер; сузить до нужного// разбор: cluster-admin у приложения = при компрометации пода атакующий получает весь кластер (создать под где угодно, читать все секреты, эскалироваться). Исправляют по least privilege: определить, какие ресурсы и глаголы приложение реально использует (kubectl auth can-i --list из-под его SA), и выдать узкую Role/ClusterRole ровно под это. Широкие роли и wildcard (*) в проде избегают.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.