
Инструмент статического анализа и визуализации Kubernetes RBAC
Анализ RBAC в Kubernetes стал простым
Krane — это простой инструмент статического анализа RBAC в Kubernetes. Он выявляет потенциальные риски безопасности в дизайне RBAC K8s и предлагает способы их устранения. Панель управления Krane показывает текущий уровень безопасности RBAC и позволяет перемещаться по его определению.
Вы можете начать работу с Krane, установив его через Helm-чарт в целевой кластер Kubernetes или запустив локально с помощью Docker.
Предполагается, что у вас установлен Helm CLI на вашей машине.```sh $ helm repo add appvia https://appvia.github.io/krane $ helm repo update $ helm install krane appvia/krane --namespace krane --create-namespace
Следуйте инструкциям вывода установки Helm-чарта о том, как настроить перенаправление портов для панели Krane.
### Запуск с Docker
Предполагается, что у вас запущен [docker](https://docs.docker.com/get-docker/) на локальной машине. Установите [docker-compose](https://docs.docker.com/compose/install/#install-compose), если вы ещё этого не сделали.
Krane зависит от RedisGraph. Стек `docker-compose` определяет всё необходимое для сборки и запуска сервиса _Krane_ локально. Он также позаботится о его зависимости [RedisGraph](https://oss.redislabs.com/redisgraph/).```
docker-compose up -d
Krane docker образ будет автоматически собран, если его ещё нет на локальной машине.
Обратите внимание, что при локальном запуске docker-compose Krane не запускает RBAC report и dashboard автоматически. Вместо этого контейнер по умолчанию будет ожидать 24 часа — это значение можно изменить в docker-compose.override.yml. Выполните вход в работающий контейнер Krane, чтобы запускать команды. Локальный docker-compose также смонтирует kube config (~/.kube/config) внутрь контейнера, что позволит вам запускать отчёты по любым кластерам Kubernetes, к которым у вас уже есть доступ.
Выполните вход в работающий контейнер Krane.```sh docker-compose exec krane bash
Оказавшись в контейнере, вы можете начать использовать команды `krane`. Попробуйте `krane -help`.```sh
krane -h
Чтобы проверить, какие службы запущены и связанные с ними порты:``` docker-compose ps
Чтобы остановить _Krane_ и его зависимые службы:```
docker-compose down
$ krane --help
NAME:
krane
DESCRIPTION:
Kubernetes RBAC static analysis & visualisation tool
COMMANDS:
dashboard Start K8s RBAC dashboard server
help Display global or [command] help documentation
report Run K8s RBAC report
GLOBAL OPTIONS:
-h, --help
Display help documentation
-v, --version
Display version information
-t, --trace
Display backtrace when an error occurs
AUTHOR:
Marcin Ciszak <[email protected]> - Appvia Ltd <appvia.io>
### Сгенерировать отчет RBAC
#### С локальным контекстом `kubectl`
Для запуска отчета для работающего кластера необходимо указать контекст _kubectl_```
krane report -k <context>
Вы также можете передать флаг -c <cluster-name>, если вы планируете запускать инструмент для нескольких кластеров и индексировать граф RBAC отдельно для каждого имени кластера.
Чтобы запустить отчет по локальным RBAC yaml/json файлам, укажите путь к каталогу``` krane report -d </path/to/rbac-directory>
ПРИМЕЧАНИЕ: _Krane_ ожидает, что в указанном пути к каталогу будут присутствовать следующие файлы (в формате YAML или JSON):
- psp
- roles
- clusterroles
- rolebindings
- clusterrolebindings
Если Политики безопасности Pod не используются, вы можете обойти это ожидание, создав файл `psp` вручную со следующим содержимым:```json
{
"items": []
}
Примечание: PodSecurityPolicy был объявлен устаревшим в Kubernetes v1.21 и удалён из Kubernetes в v1.25.
Чтобы запустить отчёт из контейнера, работающего в кластере Kubernetes``` krane report --incluster
ПРИМЕЧАНИЕ: Service account, используемый _Krane_, потребует доступа к ресурсам RBAC. См. [Предварительные требования](https://github.com/appvia/krane/blob/HEAD/k8s/one-time/prerequisites.yaml) для подробностей.
#### В CI/CD пайплайне
Чтобы проверить определение RBAC как шаг в CI/CD пайплайне```
krane report --ci -d </path/to/rbac-directory>
ПРИМЕЧАНИЕ: Krane ожидает соблюдения определенных правил именования для файлов ресурсов RBAC, хранящихся локально. См. раздел выше. Для выполнения команд krane рекомендуется, чтобы исполнитель CI ссылался на образ docker quay.io/appvia/krane:latest.
Режим CI включается флагом --ci. Krane вернет ненулевой код возврата вместе с подробностями о нарушаемых правилах риска, когда будет обнаружена одна или несколько опасностей.
Чтобы просмотреть дерево аспектов RBAC, сетевой граф и последние результаты отчета, сначала необходимо запустить сервер панели управления.``` krane dashboard
Cluster flag `-c <cluster-name>` may be passed if you want to run the dashboard against specific cluster name. Dashboard will look for data related to specified cluster name which is cached on the file system.
Command above will start local web server on default port `8000`, and display the dashboard link.
## Архитектура
### Данные RBAC, индексированные в локальной графовой базе данных
_Krane_ индексирует сущности RBAC в RedisGraph. Это позволяет нам эффективно и просто запрашивать сеть зависимостей, используя подмножество [CypherQL](https://oss.redislabs.com/redisgraph/cypher_support/), поддерживаемое [RedisGraph](https://oss.redislabs.com/redisgraph/).
#### Схема

#### Узлы
Следующие узлы создаются в графе для соответствующих объектов RBAC:
* `Psp` - Узел PSP, содержащий атрибуты политики безопасности подов. Применимо только при работе с K8s < 1.25.
* `Rule` - Узел Rule представляет правило контроля доступа к ресурсам Kubernetes.
* `Role` - Узел Role представляет заданную роль или ClusterRole. Атрибут `kind` определяет тип роли.
* `Subject` - Subject представляет всех возможных субъектов в кластере (`kind`: User, Group и ServiceAccount)
* `Namespace` - Узел Kubernetes Namespace.
#### Рёбра
* `:SECURITY` - Определяет связь между узлами Rule и Psp. Применимо только при работе с K8s < 1.25.
* `:GRANT` - Определяет связь между узлом Role и Rule, связанным с этой ролью.
* `:ASSIGN` - Определяет связь между субъектом (Subject) и заданной ролью/ClusterRole (узел Role).
* `:RELATION` - Определяет связь между двумя различными узлами субъектов (Subject).
* `:SCOPE` - Определяет связь между узлами Role и Namespace.
* `:ACCESS` - Определяет связь между узлами Subject и Namespace.
* `:AGGREGATE` - Определяет связь между ClusterRoles (одна ClusterRole агрегирует другую) `A-(aggregates)->B`
* `:COMPOSITE` - Определяет связь между ClusterRoles (одна ClusterRole может быть агрегирована в другую) `A<-(is a composite of)-B`
Все рёбра являются двунаправленными, это означает, что граф может быть запрошен в любом направлении.
Исключением являются отношения `:AGGREGATE` и `:COMPOSITE`, которые являются однонаправленными, хотя и касаются одних и тех же узлов ребра.
#### Запросы к графу
Чтобы напрямую запросить граф, вы можете войти в запущенный контейнер `redisgraph`, запустить `redis-cli` и выполнить произвольные запросы. Следуйте официальным [инструкциям](https://oss.redislabs.com/redisgraph/) для примеров [команд](https://oss.redislabs.com/redisgraph/commands/).
Вы также можете запросить граф из консоли _Krane_. Сначала войдите в запущенный контейнер _Krane_, затем```ruby
# Start Krane console - this will open interactive ruby shell with Krane code preloaded
console
# Instantiate Graph client
graph = Krane::Clients::RedisGraph.client cluster: 'default'
# Run arbitrary CypherQL query against indexed RBAC Graph
res = graph.query(%Q(
MATCH (r:Rule {resource: "configmaps", verb: "update"})<-[:GRANT]-(ro:Role)<-[:ASSIGN]-(s:Subject)
RETURN s.kind as subject_kind, s.name as subject_name, ro.kind as role_kind, ro.name as role_name))
# Print the results
res.print_resultset
+----------------+--------------------------------+-----------+------------------------------------------------+ | subject_kind | subject_name | role_kind | role_name | +----------------+--------------------------------+-----------+------------------------------------------------+ | ServiceAccount | bootstrap-signer | Role | system:controller:bootstrap-signer | | User | system:kube-controller-manager | Role | system::leader-locking-kube-controller-manager | | ServiceAccount | kube-controller-manager | Role | system::leader-locking-kube-controller-manager | | User | system:kube-scheduler | Role | system::leader-locking-kube-scheduler | | ServiceAccount | kube-scheduler | Role | system::leader-locking-kube-scheduler | +----------------+--------------------------------+-----------+------------------------------------------------+
Примечание: приведенный выше запрос выберет всех субъектов, которым назначены Roles/ClusterRoles, предоставляющие доступ к `update configmaps`.
## Конфигурация
### Правила рисков RBAC
Правила рисков RBAC определены в файле [Rules](https://github.com/appvia/krane/blob/HEAD/config/rules.yaml). Структура каждого правила в значительной степени интуитивно понятна.
Встроенный набор можно расширить/переопределить, добавив дополнительные пользовательские правила в файл [Cutom Rules](https://github.com/appvia/krane/blob/HEAD/config/custom-rules.yaml).
#### Макросы правил рисков
Макросы — это «контейнеры» для набора общих/совместно используемых атрибутов, на которые ссылаются одно или несколько правил рисков. Если вы решите использовать макрос в данном правиле рисков, вам нужно будет ссылаться на него по имени, например `macro: <macro-name>`. Обратите внимание, что атрибуты, определенные в ссылочном `macro`, имеют приоритет над теми же атрибутами, определенными на уровне правила.
Макрос может содержать любой из следующих атрибутов:
- `query` — [запрос RedisGraph](#querying-the-graph). Имеет приоритет над `template`. Требует определения `writer`.
- `writer` — Writer — это выражение Ruby, используемое для форматирования результирующего набора `query`. Writer имеет приоритет над `template`.
- `template` — имя встроенного шаблона запроса/writer. Если `query` и `writer` не указаны, то будет использован выбранный генератор запросов вместе с соответствующим writer.
#### Атрибуты правил рисков
Правило может содержать любой из следующих атрибутов:
- `id` [Обязательно] Идентификатор правила, уникальный идентификатор правила.
- `group_title` [Обязательно] Заголовок, применяемый ко всем элементам, подпадающим под эту проверку риска.
- `severity` [Обязательно] Серьезность: одно из :danger, :warning, :info.
- `info` [Обязательно] Текстовое описание проверки и предложения по снижению риска.
- `query` [Условно] [запрос RedisGraph](#querying-the-graph).
- Имеет приоритет над `template`. Требует определения `writer`.
- `writer` [Условно] Writer — это выражение Ruby, используемое для форматирования результирующего набора запроса.
- Writer имеет приоритет над `template`. Требует определения `query`.
- `template` [Условно] Имя встроенного шаблона запроса/writer. Если `query` и `writer` не указаны, будет использован выбранный генератор запросов вместе с соответствующим writer.
- Некоторые встроенные шаблоны требуют указания атрибута `match_rules` на уровне отдельного правила для построения корректного запроса. Шаблоны, которые в настоящее время требуют этого:
- **_risky-role_** — Строит графический запрос с множественным сопоставлением на основе правил доступа, указанных в `match_rules`. Сгенерированный графический запрос возвращает следующие столбцы:
- role_name
- role_kind
- namespace_name (массив (_array_) возвращается, если возвращено несколько элементов)
- `match_rules` [Условно] Требуется, когда `template` полагается на правила сопоставления для построения запроса.
- Пример:
```yaml
match_rules:
- resources: ['cronjobs']
verbs: ['update']
```
Атрибуты и значения следуют [спецификации роли Kubernetes RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-examples).
- `custom_params` [Необязательно] Список пользовательских пар ключ-значение, которые будут вычислены и заменены в представлении `query` и `writer` правила.
- Пример:
```yaml
custom_params:
- attrA: valueA
- attrB: valueB
```
Шаблонные заполнители для указанных выше ключей `{{attrA}}` и `{{attrB}}` будут заменены на `valueA` и `valueB` соответственно.
- `threshold` [Необязательно] Числовое значение. При определении становится доступным как шаблонный заполнитель `{{threshold}}` в выражении `writer`.
- `macro` [Необязательно] Ссылка на общие параметры, определенные в именованном макросе.
- `disabled` [Необязательно] При установке `true` отключает данное правило и исключает его из оценки.
По умолчанию все правила включены.
#### Примеры правил рисков
##### Явный запрос и выражение writer```yaml
- id: verbose-rule-example
group_title: Example rule
severity: :danger
info: Risk description and instructions on how to mitigate it goes here
query: |
MATCH
(s:Subject)-[:ACCESS]->(ns:Namespace)
WHERE
NOT s.name IN {{whitelist_subject_names}}
RETURN
s.kind as subject_kind,
s.name as subject_name,
COLLECT(ns.name) as namespace_names
ORDER BY
subject_kind,
subject_name,
namespace_names DESC
threshold: 2
writer: |
if result.namespace_names.count > {{threshold}}
"#{result.subject_kind} #{result.subject_name} can access namespaces: #{result.namespace_names.join(', ')}"
end
disabled: true
Приведенный выше пример явно определяет графовый query, который используется для оценки риска RBAC, и выражение writer, используемое для форматирования набора результатов запроса. Запрос просто выбирает все Subjects (исключая занесенные в белый список) и Namespaces, к которым они имеют доступ. Обратите внимание, что набор результатов будет включать только Subjects, имеющие доступ более чем к 2 Namespaces (заметили значение threshold?). Последнее выражение writer'а будет захвачено как отформатированный вывод элемента результата.
writer может получить доступ к элементу набора результатов через объект result с методами, соответствующими элементам, возвращаемым запросом, например, result.subject_kind, result.subject_name и т.д.
Примечание:
{{threshold}} в выражении writer будет заменен значением ключевого слова threshold правила.{{whitelist_subject_names}} представляет собой пользовательское поле, которое будет интерполировано с значениями Белого списка, определенными для данного id правила. Если имя поля-плейсхолдера не определено в белом списке, оно будет заменено пустым массивом [''] по умолчанию. Подробнее о белых списках читайте ниже.Встроенные шаблоны значительно упрощают определение правил риска, однако они предназначены для извлечения определенного рода информации и могут не подойти для ваших пользовательских правил. Если вы обнаружите, что используете одни и те же выражения query или writer в нескольких правилах, вам следует извлечь их в macro и ссылаться на него в своих пользовательских правилах, чтобы избежать дублирования.```yaml
Пример выше показывает одно из встроенных правил. Оно ссылается на шаблон `risky-role`, который при обработке расширит правило, вставляя выражения `query` и `writer` перед срабатыванием оценки правила. `match_rules` будет использован для построения соответствующего запроса соответствия.
### Белый список рисков RBAC
Необязательный белый список содержит набор пользовательских имен атрибутов и соответствующие (внесенные в белый список) значения.
#### Атрибуты белого списка
Имена атрибутов и их значения произвольны. Они определены в файле [Whitelist](https://github.com/appvia/krane/blob/HEAD/config/whitelist.yaml) и разделены на три отдельных раздела:
- `global` - Область верхнего уровня. Определенные здесь пользовательские атрибуты будут применяться ко всем правилам риска независимо от имени кластера.
- `common` - Пользовательские атрибуты будут ограничены конкретным идентификатором правила риска `id` независимо от имени кластера.
- `cluster` (с вложенным списком имен кластеров) - Пользовательские атрибуты будут применяться к конкретному идентификатору правила риска `id` для данного имени кластера.
Каждое [правило риска](#rbac-risk-rules) при оценке попытается интерполировать все заполнители параметров, используемые в `query`, например `{{your_whitelist_attribute_name}}`. Если имя параметра-заполнителя (т.е. имя между двойными фигурными скобками) совпадает с любым из имен атрибутов белого списка для этого идентификатора правила риска `id`, оно будет заменено на вычисленное значение.
Если для данного заполнителя значения не найдены, он будет заменен на `['']`.
#### Примеры белого списка
Пример белого списка ниже создает следующее сопоставление `placeholder-key => value` для [правила риска](#rbac-risk-rules) со значением атрибута `id`, совпадающим с _some-risk-rule-id_```
{{whitelist_role_names}} => ['acp:prometheus:operator']
{{whitelist_subject_names}} => ['privileged-psp-user', 'another-user']
Приведённые выше ключи-заполнители при использовании в пользовательских графовых запросах будут заменены на соответствующие значения при оценке Правила риска.
rules: global: # global scope - applies to all risk rule and cluster names whitelist_role_names: # custom attribute name - acp:prometheus:operator # custom attribute values
common: # common scope - applies to specific risk rule id regardless of cluster name some-risk-rule-id: # this corresponds to risk rule id defined in config/rules.yaml whitelist_subject_names: # custom attribute name - privileged-psp-user # custom attribute values
cluster: # cluster scope - applies to speciifc risk rule id and cluster name default: # example cluster name some-risk-rule-id: # risk rule id whitelist_subject_names: # custom attribute nane - another-user # custom attribute values
## Развертывание в Kubernetes
_Krane_ можно легко развернуть в локальном или удаленном кластере Kubernetes.
### Предварительные требования K8s
Пространство имен Kubernetes, сервисный аккаунт и соответствующий RBAC должны присутствовать в кластере. См. [Предварительные требования](https://github.com/appvia/krane/blob/HEAD/k8s/one-time/prerequisites.yaml) для справки.
Точка входа _Krane_ по умолчанию выполняет [bin/in-cluster-run](https://github.com/appvia/krane/blob/HEAD/bin/in-cluster-run), который ожидает доступности экземпляра RedisGraph перед запуском цикла _отчета_ RBAC и веб-сервера _панели управления_.
Вы можете управлять некоторыми аспектами выполнения в кластере с помощью следующих переменных окружения:
* `KRANE_REPORT_INTERVAL` - Определяет интервал в секундах для запуска отчета статического анализа RBAC. По умолчанию: `300` (в секундах, т.е. 5 минут).
* `KRANE_REPORT_OUTPUT` - Определяет формат вывода отчета о рисках RBAC. Возможные значения: `:json`, `:yaml`, `:none`. По умолчанию: `:json`.
### Локальный или удаленный кластер K8s
#### Helm Chart
Прежде чем начать, вам понадобятся следующие инструменты:
* [Helm CLI](https://helm.sh/docs/intro/install/)
Установите Helm Chart:```sh
$ helm repo add appvia https://appvia.github.io/krane
$ helm repo update
$ helm install krane appvia/krane --namespace krane --create-namespace
См. файл values.yaml для получения подробной информации о других настраиваемых параметрах и опциях.
kubectl create
--context
--namespace krane
-f k8s/redisgraph-service.yaml
-f k8s/redisgraph-deployment.yaml
-f k8s/krane-service.yaml
-f k8s/krane-deployment.yaml
Обратите внимание, что _Krane_ панель управления сервисом не доступна по умолчанию!```sh
kubectl port-forward svc/krane 8000 \
--context=<docker-desktop> \
--namespace=krane
# Open Krane dashboard at http://localhost:8000
Вы можете найти примеры манифестов развертывания в каталоге k8s.
Изменяйте манифесты по мере необходимости для ваших развертываний, убедившись, что вы ссылаетесь на правильную версию Docker-образа Krane в его файле развертывания. Смотрите реестр Docker-образов Krane для доступных тегов или просто используйте latest.
Если ваш кластер K8s имеет встроенную поддержку контроллера Compose-on-Kubernetes (docker-desktop поддерживает это по умолчанию), то вы можете развернуть Krane и его зависимости одной командой docker stack:```sh
docker stack deploy
--orchestrator kubernetes
--namespace krane
--compose-file docker-compose.yml
--compose-file docker-compose.k8s.yml krane
Примечание: Убедитесь, что ваш текущий контекст kube установлен правильно перед выполнением указанной выше команды!
Приложение Stack должно быть теперь развернуто в кластере Kubernetes, и все сервисы готовы и открыты. Обратите внимание, что _Krane_ автоматически запустит свой цикл отчетов и сервер панели управления.```sh
docker stack services --orchestrator kubernetes --namespace krane krane
Команда выше выдаст следующий вывод:``` ID NAME MODE REPLICAS IMAGE PORTS 0de30651-dd5 krane_redisgraph replicated 1/1 redislabs/redisgraph:1.99.7 *:6379->6379/tcp aa377a5f-62b krane_krane replicated 1/1 quay.io/appvia/krane:latest *:8000->8000/tcp
Проверьте состояние безопасности RBAC вашего кластера Kubernetes, посетив http://localhost:8000.
Обратите внимание, что для развертываний на удаленных кластерах, вероятно, потребуется сначала выполнить проброс портов (port-forward) сервиса _Krane_.```sh
kubectl --context=my-remote-cluster --namespace=krane port-forward svc/krane 8000
Чтобы удалить стек```sh
docker stack rm krane
--orchestrator kubernetes
--namespace krane
## Уведомления
Krane будет уведомлять вас об обнаруженных аномалиях средней и высокой степени серьезности через интеграцию со Slack.
Чтобы включить уведомления, укажите `webhook_url` Slack и `channel` в файле [config/config.yaml](https://github.com/appvia/krane/blob/HEAD/config/config.yaml), или, альтернативно, установите обе переменные окружения `SLACK_WEBHOOK_URL` и `SLACK_CHANNEL`. Переменные окружения будут иметь приоритет над значениями из файла конфигурации.
## Локальная разработка
В этом разделе описаны шаги для настройки локальной разработки.
### Настройка
Установите зависимости кода _Krane_ с помощью```sh
./bin/setup
Krane зависит от RedisGraph. docker-compose — это самый быстрый способ запустить зависимости Krane локально.```sh
docker-compose up -d redisgraph
Чтобы проверить, что служба RedisGraph работает:```sh
docker-compose ps
Чтобы остановить сервисы:```sh docker-compose down
### Разработка
На данном этапе вы сможете изменять код _Krane_ и проверять результаты, выполняя команды в локальной оболочке.```sh
$ ./bin/krane --help # to get help
$ ./bin/krane report -k docker-desktop # to generate your first report for
# local docker-desktop k8s cluster
...
Чтобы включить режим локальной разработки Dashboard UI```sh $ cd dashboard $ npm install $ npm start
Это автоматически запустит сервер Dashboard, откроет браузер по умолчанию и будет отслеживать изменения исходных файлов.
_Krane_ поставляется с предварительной настройкой для улучшения взаимодействия с разработчиком с помощью [Skaffold](https://skaffold.dev/). Теперь стало проще итерировать проект и проверять приложение, запуская весь стек в локальном или удаленном кластере Kubernetes.
Горячая перезагрузка кода позволяет автоматически распространять локальные изменения на работающий контейнер для ускорения жизненного цикла разработки.```sh
skaffold dev --kube-context docker-desktop --namespace krane --port-forward
Запустите тесты локально с```sh bundle exec rspec
## Внесение вклада в Krane
Мы приветствуем любые вклады от сообщества! Ознакомьтесь с нашим руководством по [внесению вклада](https://github.com/appvia/krane/blob/HEAD/CONTRIBUTING.md), чтобы узнать, как начать. Если вы используете _Krane_, находите его полезным или просто интересуетесь безопасностью Kubernetes, дайте нам знать, **поставив звезду** и **отслеживая** этот репозиторий. Спасибо!
## Присоединяйтесь
Присоединяйтесь к обсуждению в нашем [канале сообщества](https://www.appvia.io/join-the-appvia-community).
Krane — это проект сообщества, и мы приветствуем ваш вклад. Чтобы сообщить об ошибке, предложить улучшение или запросить новую функцию, пожалуйста, откройте issue на GitHub. Обратитесь к нашему руководству по [внесению вклада](https://github.com/appvia/krane/blob/HEAD/CONTRIBUTING.md) для получения дополнительной информации о том, как вы можете помочь.
## Дорожная карта
Смотрите нашу [дорожную карту](https://github.com/appvia/krane/projects/1), чтобы узнать подробности о наших планах по проекту.
## Лицензия
Автор: Marcin Ciszak <[email protected]>
Copyright (c) 2019-2020 [Appvia Ltd](https://appvia.io)
Этот проект распространяется под лицензией [Apache License, Version 2.0](https://github.com/appvia/krane/blob/HEAD/LICENSE).