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 в надежный рубеж обороны.

