Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи?

Написать
Войти
Дайджесты новостей
Иллюстрация к статье об уязвимости nodes/proxy GET и защите Kubelet в Kubernetes

nodes/proxy GET в Kubernetes: почему одно лишнее право в RBAC открывает тихий RCE во всех подах кластера

Уязвимость в обработке WebSocket-соединений Kubelet позволяет любому ServiceAccount с правом nodes/proxy GET негласно выполнять команды внутри любого пода кластера в обход аудит-логов: разбор механизма атаки, затронутых компонентов уровня Rook-Ceph и превентивных мер через KEP-2862, Kyverno и Cilium.

nodes/proxy GET в Kubernetes: почему одно лишнее право в RBAC открывает тихий RCE во всех подах кластера

В модели разграничения доступа Kubernetes (RBAC) права на чтение традиционно воспринимаются как безопасные. Однако субресурс nodes/proxy представляет собой опасное исключение: предоставление глагола get для этого эндпоинта фактически наделяет сервисный аккаунт правами суперпользователя на целевом узле. Архитектурная особенность протокола взаимодействия с Kubelet позволяет выполнять произвольные команды внутри любых контейнеров без фиксации стандартных событий в журнале аудита API-сервера.

Механизм бреши: как HTTP GET превращается в удаленное исполнение кода

Диспетчер узла Kubelet работает как локальный демон на каждой ноде кластера, публикуя защищенный HTTPS-интерфейс на порту 10250. Этот эндпоинт отвечает за запуск контейнеров, сбор системных метрик и точку входа /exec для интерактивного исполнения команд. Чтобы клиент мог обратиться к Kubelet через центральную точку входа, API-сервер Kubernetes предоставляет субресурс проксирования nodes/proxy.

Уязвимость кроется в сопоставлении HTTP-методов и прав RBAC. Для интерактивного выполнения команды (kubectl exec) клиент использует постоянное двунаправленное WebSocket-соединение. Протокол WebSocket начинает сессию со стандартного HTTP-запроса GET с заголовком Upgrade: websocket. Когда API-сервер проксирует такой запрос к Kubelet, подсистема авторизации сопоставляет HTTP GET с RBAC-глаголом get для ресурса nodes/proxy.

В результате проверка прав успешно проходит, но вторичная авторизация операции создания потока (create), которая обычно требуется для эндпоинта /exec, на уровне Kubelet не запрашивается. Прямой канал взаимодействия с Kubelet на порту 10250 позволяет запустить команду в любом контейнере пространства имен узла с помощью простой утилиты работы с WebSocket (websocat) и токена сервисного аккаунта.

[ServiceAccount Token] ──> [API Server Proxy: GET nodes/proxy] ──> [Kubelet :10250 /exec (Upgrade: websocket)] ──> [RCE в целевом контейнере]
                                                                        └──> Обход Audit Log API-сервера

Поскольку трафик идет напрямую через проксирующий туннель к Kubelet, центральный API-сервер не создает структурированных записей аудита о вызове команды внутри контейнера. Отсутствие событий exec в audit log создает иллюзию безопасности, хотя активность может быть зафиксирована локальными системными журналами узла или EDR-агентами.

Зона поражения: операторы хранилищ и коллекторы метрик

Право nodes/proxy исторически запрашивалось многими популярными Helm-чартами и операторами, которым требовался доступ к локальной статистике Kubelet или логам подов. Масштабный аудит экосистемы выявил наличие этого разрешения в десятках инфраструктурных компонентов.

Особую опасность представляет композиция прав в операторах уровня хранилища. Например, оператор Rook-Ceph (rook-ceph-system) совмещал разрешение nodes/proxy GET с доступом к секретам кластера (secrets GET/LIST/WATCH). В случае компрометации пода оператора злоумышленник получает возможность прочитать ключи шифрования дисков LUKS и учетные данные Ceph, а затем через скрытый RCE в системных подах узла развить атаку до захвата управляющего хранилища etcd. В версии Rook v1.19.1 (PR #16979) избыточное право было удалено.

Аналогичная проблема затрагивала OpenTelemetry Collector и OpenTelemetry Operator. Агентам сбора телеметрии требовался доступ к эндпоинтам метрик /stats/summary и /metrics/cadvisor, но из-за отсутствия гранулярных разрешений в старых версиях Kubernetes разработчики были вынуждены запрашивать полный субресурс nodes/proxy.

КомпонентНазначение доступаРиск старой конфигурацииСтатус исправления
Rook-CephУправление дисками и хранилищемRCE + чтение ключей Ceph и secretsИсправлено в v1.19.1 (PR #16979)
OpenTelemetry CollectorСбор метрик контейнеров и cAdvisorНодовый RCE через права мониторингаПереход на nodes/stats и nodes/metrics
OpenTelemetry OperatorАвтоматическая инструментация подовКомпрометация контроллера узлаШаблонизация RBAC под KEP-2862

Эволюция авторизации: KEP-2862 и разделение Kubelet API

Фундаментальным решением проблемы стала инициатива Kubernetes SIG Node и SIG Auth — предложение по улучшению KEP-2862 (Fine-Grained Kubelet API Authorization). Механизм вводит гранулярные субресурсы для безопасного разграничения прав Kubelet вместо монолитного nodes/proxy:

  • nodes/stats — доступ к статистике использования ресурсов узла и подов;
  • nodes/metrics — сбор метрик для систем мониторинга (Prometheus, OpenTelemetry);
  • nodes/log — чтение системных журналов контейнеров;
  • nodes/spec — получение конфигурационной спецификации ноды;
  • nodes/pods — чтение списка запущенных подов и состояния их готовности.

Инициатива прошла путь от альфа-версии в Kubernetes 1.32 до статуса Beta (включена по умолчанию) в версии 1.33 и финального перехода в статус общедоступной функциональности (GA) в релизе Kubernetes 1.36. В современных кластерах агентам мониторинга достаточно выдать целевые права nodes/metrics или nodes/stats, полностью исключив доступ к эндпоинтам выполнения команд.

Практический план защиты и аудит кластера

Устранение риска требует комплексного подхода, сочетающего инвентаризацию ролей, внедрение политик допуска и сетевую изоляцию портов управления.

1. Инвентаризация ролей и сервисных аккаунтов

Первым шагом необходимо выявить все роли (ClusterRole, Role) и сервисные аккаунты, обладающие правом nodes/proxy. Базовую проверку конкретного аккаунта можно выполнить штатной командой:

kubectl auth can-i get nodes --subresource=proxy --as=system:serviceaccount:<namespace>:<serviceaccount>

Для полной инвентаризации следует выгрузить все манифесты ролей и сопоставить назначенные права с реальными потребностями приложений:

kubectl get clusterroles,roles -A -o json | jq -r   '.items[] | select(.rules[]? | select(.resources[]? == "nodes/proxy" or .resources[]? == "nodes/*")) | .metadata.name'
2. Превентивные политики Kyverno

Чтобы исключить повторное появление небезопасных ролей при установке сторонних Helm-чартов, рекомендуется внедрить декларативную политику валидации Admission Controller на базе Kyverno.

Развертывание политики проводится в два этапа: сначала в режиме Audit для выявления существующих нарушений без блокировки развертываний, а после адаптации чартов — в режиме Enforce:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-clusterrole-nodesproxy
spec:
  validationFailureAction: Audit
  rules:
    - name: check-nodes-proxy
      match:
        any:
          - resources:
              kinds:
                - ClusterRole
                - Role
      validate:
        message: "Использование субресурса nodes/proxy запрещено. Используйте гранулярные права KEP-2862."
        pattern:
          =(rules):
            - =(resources):
                - "!nodes/proxy"
                - "!nodes/*"
3. Эшелонированная сетевая защита через CiliumNetworkPolicy

В качестве дополнительного барьера безопасности (Defense in Depth) следует ограничить сетевую доступность порта Kubelet 10250 на уровне eBPF. Для подов приложений, которым не требуется опрашивать Kubelet API, исходящий трафик (egress) к порту 10250 узлов должен быть заблокирован:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: deny-kubelet-port-egress
  namespace: default
spec:
  endpointSelector:
    matchLabels:
      app.kubernetes.io/part-of: general-workloads
  egress:
    - toEntities:
        - host
        - remote-node
      toPorts:
        - ports:
            - port: "10250"
              protocol: TCP
          rules:
            deny: true
4. Мониторинг SubjectAccessReview

Для оперативного обнаружения попыток несанкционированной эскалации привилегий необходимо настроить оповещения системы мониторинга на всплески запросов SubjectAccessReview к субресурсу nodes/proxy. Любое обращение к этому эндпоинту от сервисных аккаунтов, не входящих в утвержденный белый список системных компонентов, должно инициировать проверку безопасности.

Разделение полномочий Kubelet в сочетании с жесткими политиками admission и сетевой фильтрацией устраняет скрытый вектор компрометации, превращая Kubernetes RBAC в надежный рубеж обороны.