Выход за пределы pod'а в Kubernetes через монтирование логов

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.
Прим. перев.: Эта заметка была написана исследователем ИТ-безопасности из компании Aqua Security, специализирующейся на DevSecOps. Она является прекрасной иллюстрацией тех тонкостей в конфигурации Kubernetes, что важно всегда держать в голове, обслуживая кластеры в production. Конечно, если вы думаете про их безопасность…



Kubernetes состоит из множества компонентов, и иногда их комбинирование определенным образом приводит к неожиданным результатам. В этой статье я покажу, как pod, запущенный с привилегиями root'а и примонтированной директорией /var/log узла, может раскрыть содержимое всей файловой системы хоста пользователю с доступом к его логам. Мы также обсудим варианты решения этой проблемы.

Как Kubernetes видит логи


Задумывались ли вы над тем, как kubectl logs <pod_name> извлекает логи из pod'а? Кто отвечает за сбор логов из контейнеров? И как они попадают на ваш компьютер?

Следующая схема иллюстрирует процесс:



Kubelet создает структуру внутри директории /var/log на хосте, представляющую pod'ы на узле. В директории для нашего pod'а есть файл 0.log (1), но на самом деле это симлинк на лог контейнера, лежащий в /var/lib/docker/containers. Это все с точки зрения хоста.

Kubelet открывает endpoint /logs/ (2), который просто работает с файловым HTTP-сервером в директории (3), делая логи доступными для запросов, поступающих от API-сервера.

Теперь представьте, что мы развернули pod с hostPath, примонтированным в /var/log. У такого pod'а будет доступ ко всем лог-файлам на хосте. Хотя уже это само по себе является потенциальной проблемой, мы можем сделать следующий логический шаг. Что, если заменить 0.log на симлинк к… скажем, /etc/shadow?

│
├── var
│ ├── logs
│ │ ├── pods
│ │ │ ├── default_mypod_e7869b14-abca-11e8-9888-42010a8e020e
│ │ │ │ ├── mypod
│ │ │ │ │ ├── 0.log -> /etc/shadow
│ │ │ │ │ │

Теперь, пытаясь загрузить логи с помощью kubectl logs на клиентской машине, получим:

$ kubectl logs mypod
failed to get parse function: unsupported log format: "root:*:18033:0:99999:7:::\n"

Kubelet переходит по ссылке и читает содержимое файла, на который она указывает (им может быть любой файл на узле).

Поскольку ожидался JSON, kubectl вылетел после первой строки, однако мы можем легко прочитать конкретные строки файла shadow, запуская команду с флагом –-tail=-<line_number>.

Это поразительно. Поскольку kubelet переходит по симлинку, можно воспользоваться его root-правами для чтения любого файла на узле, просто создавая символическую ссылку внутри pod'а.

Побег из pod'а


Пойдем еще дальше. Мы знаем, что при запуске pod'а в Kubernetes в него устанавливается токен ServiceAccount. Таким образом, если service account разрешает доступ к логам, мы можем напрямую получить доступ к kubelet'у и root-привилегии на узле.

Я написал proof of concept (POC), демонстрирующий данный вектор атаки:

  • развертывание pod'а с точкой монтирования /var/log;
  • создание символической ссылки на корневую директорию хоста;
  • чтение закрытого ключа ssh пользователя на хосте.

В следующем видео показаны две особые команды, выполняющиеся внутри pod'а:

  • lsh == ls (на файловой системе хоста);
  • cath == cat (на файловой системе хоста).

(Прим. перев.: К сожалению, на хабре так и не починили вставку контента с asciinema, хотя мы уже обращались по данной проблеме, поэтому вынуждены «вставлять» видео простой ссылкой выше.)

Все файлы, задействованные в этом POC, можно найти в соответствующем репозитории GitHub. Там же лежит еще один POC-скрипт, который автоматически собирает закрытые ключи и токены ServiceAccount с файловой системы хоста.

Монтирование директорий может быть опасным


Итак, это уязвимость или просто плохая практика?

Развертывание pod'а с открытым для записи hostPath в /var/log встречается редко (кроме того, есть другие способы злоупотребить монтированием секретных директорий хоста в pod). Но даже если вы знали, что монтирование /var/log — сомнительная практика, вы скорее всего не ожидали, что она позволит с такой легкостью завладеть узлом.

Перед публикацией мы обратились в команду Kubernetes по безопасности, чтобы узнать, считают ли они это уязвимостью. Они пришли к выводу, что это всего лишь печальное последствие монтирования закрытой директории хоста с правами записи: риски, связанные с этим, хорошо задокументированы. Впрочем, этой уязвимостью довольно легко воспользоваться. В мире есть множество проектов, которые пользуются данным монтированием. Если вы используете один из этих проектов, помните, что ваш deployment будет уязвим перед таким способом захвата хоста.

Этот метод был протестирован на Kubernetes 1.15 и 1.13, но скорее всего затрагивает и другие версии.

Устранение


Такой «побег» возможен только в том случае, если pod работает под root'ом. Вообще этого следует избегать. Aqua CSP позволяет с минимумом усилий задать политику, предотвращающую работу контейнеров под root'ом или выдающую разрешения только определенной группе образов, для которой действительно требуется root.

Другой способ — просто не развертывать pod'ы с hostPath с правами записи в /var/log. Этот подход не задается по умолчанию и не является обычной практикой, поэтому необходимо сознательно его определять (впрочем, возможность по-прежнему остается). Но как проверить?

Мы добавили новый скрипт (hunter) в kube-hunter — наш легковесный Open Source-инструмент для тестирования Kubernetes, — проверяющий кластер на предмет существования pod'ов с такими опасными точками монтирования. (Прим. перев.: Kube-hunter присутствовал в недавнем обзоре утилит для безопасности K8s, что мы публиковали в своем блоге.)

Пользователи Aqua могут защититься от данного риска, используя runtime-политику для запрета монтирования определенных томов:



Итог


Kubernetes — сложная система с массой тонкостей в настройке безопасности, которые не всегда очевидны для рядового и даже опытного пользователя. В этой статье я показал, как при определенных обстоятельствах невинное ведение логов может привести к потенциальной уязвимости. В большинстве случаев это невозможно, однако Kubernetes предлагает пользователям большую свободу действий, которые могут отразиться на безопасности. Важно помнить об этом и внедрять соответствующие средства контроля, позволяющие предотвратить подобные ошибки.

P.S. от переводчика


Читайте также в нашем блоге:

  • «В 19% популярнейших Docker-образов нет пароля для root»;
  • «33+ инструмента для безопасности Kubernetes»;
  • «Введение в сетевые политики Kubernetes для специалистов по безопасности»;
  • «Docker и Kubernetes в требовательных к безопасности окружениях»;
  • «9 лучших практик по обеспечению безопасности в Kubernetes»;
  • «11 способов (не) стать жертвой взлома в Kubernetes».
Источник: https://habr.com/ru/company/flant/blog/466625/


Интересные статьи

Интересные статьи

Привет, Хабр!Пару недель назад мы выпустили второй мажорный релиз TeamCity в этом году. У нас давно не было таких мощных обновлений — под катом я расскажу, что новог...
Прим. перев.: В этой статье компания Banzai Cloud делится примером использования её специальных утилит для облегчения эксплуатации Kafka в рамках Kubernetes. Приводимые инструкции иллюстрируют, к...
От скорости сайта зависит многое: количество отказов, брошенных корзин. Согласно исследованию Google, большинство посетителей не ждёт загрузки больше 3 секунд и уходит к конкурентам. Бывает, что сайт ...
У WG21 есть строгий график (см. P1000) выпуска стандарта каждые три года. И никаких задержек. В течение каждого цикла мы регулярно получаем вопросы «ну почему так строго?», особенно от новых...
Сейчас мы расскажем, почему, начиная с версии 12.1, GitLab перестанет поддерживать MySQL. В июле 2017 GitLab предупредил, что прекратит поддержку MySQL. Так вот, с выпуском 12.1 период подгото...