image Привет, Хаброжители! Во многих организациях приложения работают в нативных облачных средах, обеспечивая масштабируемость и отказоустойчивость с помощью контейнеров и средств координации. Как участнику команды Ops, DevOps или даже DevSecOps, отвечающему за настройку подобной среды для своей компании, гарантировать безопасность развертываемых приложений? Как специалисту по безопасности, имеющему опыт использования традиционных систем на основе серверов или виртуальных машин, адаптировать свои знания к контейнерному развертыванию? И что разработчику нативных облачных приложений стоит учесть, чтобы повысить безопасность своих контейнеризованных приложений? В данной книге описаны ключевые технологии, лежащие в основе контейнеров и нативного облачного программирования. Поэтому после ее прочтения вы сможете лучше оценить риски для безопасности и решения, подходящие для конкретной среды, а также избежать нерекомендуемых приемов, которые подвергают опасности технологии, развернутые вами.

С помощью этой книги вы изучите многие базовые технологии и механизмы, часто применяемые в контейнерных системах, а также способы их построения в операционной системе Linux. Вместе мы углубимся в основы функционирования контейнеров и их взаимодействия и ответим не только на вопрос «что» относительно безопасности контейнеров, но и, главное, «почему». При написании данной книги я ставила перед собой цель помочь читателю лучше разобраться в происходящем при развертывании контейнеров. Мне хотелось бы вдохновить вас на создание ментальных моделей, позволяющих вам самостоятельно оценить потенциальные риски для безопасности при развертывании.

В основном в этой книге обсуждаются контейнеры приложений, которые в настоящее время используют многие организации, чтобы запускать свои приложения в таких системах, как Kubernetes и Docker, а не контейнеры для систем наподобие LXC и LXD из проекта Linux Containers Project (https://linuxcontainers.org/). В контейнере приложений можно запускать неизменяемые контейнеры с помощью кода объемом не больше, чем требуется для запуска приложения. Наряду с этим в среде системного контейнера выполняется полный дистрибутив Linux, и работают с ним скорее как с виртуальной машиной. Вполне допустимо подключаться к системному контейнеру по SSH. Если же вы захотите подключиться по SSH к контейнеру приложений, то специалисты по их безопасности посмотрят на вас косо (по причинам, изложенным далее в этой книге). Впрочем, основные механизмы создания контейнеров для систем и приложений совпадают: контрольные группы, пространства имен и изменение корневого каталога. Так что фундамент, заложенный в данной книге, позволит вам и далее изучать разницу в подходах, которые используются в различных проектах контейнеров.

Угрозы безопасности контейнеров


За последние несколько лет масштабы применения контейнеров резко возросли. Соответствующие концепции контейнеров существовали еще за несколько лет до Docker. Но большинство наблюдателей сходятся во мнении, что именно появление в 2013 году Docker с его удобными в использовании утилитами командной строки дало толчок популярности контейнеров среди сообщества разработчиков.

У контейнеров есть множество достоинств. Как гласит рекламный слоган Docker, с их помощью можно «создать один раз, выполнять где угодно» — благодаря объединению в пакет приложения и всех его зависимостей и изоляции приложения от остальной части машины, на которой оно работает. У контейнеризованного приложения есть все необходимое, его легко можно упаковать в образ контейнера, который будет работать одинаково на моем/вашем ноутбуке и на сервере в центре обработки данных (ЦОД).

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

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

С точки зрения безопасности контейнеризованная среда во многом схожа с обычным развертыванием. Нарушители пытаются похитить данные, или изменить поведение системы, или, скажем, использовать вычислительные ресурсы других людей для майнинга криптовалюты. При переходе к контейнерам ничего из этого не меняется. Однако контейнеры существенно меняют способ работы приложений, что приводит к иному набору рисков для безопасности.

Риски, угрозы и уменьшение их последствий

Риск (risk) — это потенциальная проблема, а также ее возможные последствия.

Угроза (threat) — путь реализации этого риска.

Уменьшение их последствий (mitigation) — контрмеры, с помощью которых можно предотвратить угрозу или по крайней мере уменьшить вероятность ее успешной реализации.

Скажем, существует риск, что какой-нибудь злоумышленник украдет из вашего дома ключи от вашей же машины и уедет на ней. Угрозы в данном случае — это различные способы кражи ключей: разбить окно, запустить руку и схватить ключи; просунуть удочку через щель для почты; постучать в дверь и отвлечь вас, пока сообщник быстро проскользнет внутрь и схватит ключи. Чтобы уменьшить последствия всех этих угроз, можно, например, убрать ключи от машины с видного места.

Риски очень различаются в разных организациях. Основной риск для банка, хранящего клиентские деньги, — их кража. Для интернет-магазина основная головная боль — мошеннические транзакции. Ведущий личный блог пользователь может бояться, например, что кто-то взломает его учетную запись, выдаст себя за него и начнет публиковать непристойные комментарии. В разных странах законодательство о защите персональной информации имеет свои особенности, поэтому различается и риск утечки личных данных пользователей — во многих странах риски «лишь» репутационные, в то время как в Европе Общий регламент защиты персональных данных (General Data Protection Regulation, GDPR) допускает штрафы до 4 % общего дохода компании (https://oreil.ly/guQg3).

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

Моделирование угроз (threat modeling) — процесс распознавания и перечисления возможных угроз системе. За счет планомерного анализа ее компонентов и вероятных векторов атаки модель угроз помогает определить места системы, наиболее уязвимые для атак.

Единой всеобъемлющей модели угроз не существует, все зависит от рисков конкретной среды, организации и запускаемых приложений. Но можно перечислить некоторые потенциальные угрозы, общие для многих, если не всех, контейнерных развертываний.

Модель угроз для контейнеров

В частности, модель угроз можно рассматривать с точки зрения ее участников, в число которых могут входить:

  • внешние нарушители (external attackers), пытающиеся извне получить доступ к развернутой системе;
  • внутренние нарушители (internal attackers), сумевшие получить доступ к некой части развернутой системы;
  • внутренние действующие лица-злоумышленники (malicious internal actors), например, разработчики и администраторы с определенным уровнем полномочий доступа к развернутой системе;
  • небрежные внутренние действующие лица (inadvertent internal actors), которые могут неумышленно вызывать проблемы;
  • процессы приложений (application processes) — не люди-злоумышленники, тем не менее имеющие определенный программный доступ к системе.

Необходимо учитывать набор прав доступа каждого из действующих лиц.

  • Какой доступ к системе есть у этого лица в соответствии с его учетными данными? Например, есть ли у него доступ к пользовательским учетным записям на машинах хостов, где работает развернутая система?
  • Какие права доступа оно имеет в системе? В Kubernetes этот пункт относится к настройкам управления доступом для всех пользователей, в том числе анонимных, на основе ролей.
  • Какие права доступа к сети есть у этого лица? Например, какие части системы включены в виртуальное частное облако (virtual private cloud, VPC)?

Существует несколько возможных путей атаки на развернутую контейнеризованную систему, и чтобы их систематизировать, можно, например, проанализировать потенциальные векторы атак на каждом из этапов жизненного цикла контейнера (рис. 1.1).

image

Рассмотрим эти векторы более подробно.

  • Уязвимый код. Жизненный цикл приложения начинается с написания разработчиком его кода. Он, равно как и его зависимости, может содержать изъяны (уязвимости). Существуют списки из тысяч известных уязвимостей, которыми (если они есть в приложении) могут воспользоваться злоумышленники. Образы необходимо анализировать, как вы увидите в главе 7, чтобы не применять контейнеры с известными уязвимостями. Причем делать это нужно регулярно, поскольку уязвимости обнаруживаются в уже существующем коде постоянно. В процессе анализа должны также выявляться контейнеры с устаревшим ПО, которое необходимо обновить, установив исправления безопасности. Кроме того, есть анализаторы, способные выявлять встроенное в образы вредоносное программное обеспечение.
  • Плохо настроенные образы контейнеров. Написанный код встраивается в образ контейнера. В ходе конфигурации сборки образа контейнера возникает множество возможностей создать уязвимости, которые открывают дорогу для дальнейших атак на работающий контейнер. В их число входит выполнение контейнера от имени суперпользователя, в результате чего у него оказывается больше полномочий, чем нужно. Больше информации об этом — в главе 6.
  • Атаки на систему сборки. Если злоумышленник может изменить сборку образа контейнера или как-то повлиять на нее, то сможет вставить вредоносный код, который потом будет запущен в среде промышленной эксплуатации. Кроме того, возможность закрепиться внутри среды сборки — плацдарм для злоумышленника, позволяющий в дальнейшем проникать в среду промышленной эксплуатации. Этот вопрос также обсуждается в главе 6.
  • Атаки на цепь поставок. Собранный образ контейнера сохраняется в реестре, откуда извлекается перед запуском. Как гарантировать соответствие извлекаемого образа тому, который был ранее помещен в реестр? Не могли ли злоумышленники внести в него изменения? Любой, кто может заменить образ или модифицировать его в промежутке между сборкой и развертыванием, сможет выполнить любой код в развернутой системе.
  • Плохо настроенные контейнеры. Как мы обсудим в главе 9, контейнер можно запустить с настройками, в результате которых у них появляются ненужные, а порой и незапланированные полномочия. Скачивая файлы конфигурации YAML из Интернета, пожалуйста, не запускайте их, не убедившись в отсутствии в них небезопасных настроек!
  • Уязвимые хосты. Контейнеры выполняются на хост-компьютерах, поэтому нужно проверять работающий на них код на наличие уязвимостей (например, отслеживать старые версии компонентов механизма координации, с известными уязвимостями). Имеет смысл уменьшить до минимума объем запущенного на каждом хосте программного обеспечения, чтобы сократить поверхность атаки. Кроме того, необходимо задать правильную конфигурацию хостов в соответствии с практическими рекомендациями по обеспечению безопасности. Все это обсуждается в главе 4.
  • Общедоступные секретные данные. Чтобы взаимодействовать с другими компонентами системы, код приложения часто требует учетные данные, токены или пароли. При развертывании в контейнере эти секретные значения необходимо передавать в контейнеризованный код. Как вы увидите в главе 12, существует несколько различных вариантов решения этой задачи, имеющих разную степень безопасности.
  • Незащищенная сеть. Контейнерам обычно требуется взаимодействовать друг с другом или с окружающим миром. В главе 10 обсуждается передача данных по сети в контейнерах, а в главе 11 — установление защищенных соединений между компонентами.
  • Уязвимости выхода за рамки контейнера. Широко используемые среды выполнения контейнеров, включая containerd и CRI-O, уже хорошо проверены в деле, однако не исключено, что в них все же остаются программные ошибки, вследствие которых вредоносный код, работающий внутри контейнера, может просочиться за пределы контейнера, в хост. Одна из таких проблем — Runcescape (https://oreil.ly/cFSaJ) — обнаружилась в 2019 году. В главе 4 можно прочитать об изоляции, предназначенной для ограничения кода приложения рамками контейнера. Ущерб от выхода за рамки контейнера для ряда приложений может быть столь велик, что имеет смысл задуматься о применении более эффективных механизмов изоляции, таких как те, которые обсуждаются в главе 8.

Некоторые векторы атак выходят за рамки данной книги.

  • Исходный код обычно хранится в репозиториях, потенциально доступных для атак, цель которых — взлом приложения. Необходимо обеспечить должный контроль доступа пользователя к репозиторию.
  • Хост-компьютеры связываются между собой сетью, обычно подключенной к Интернету, причем в целях безопасности при этом часто применяется VPC. Как и при обычном развертывании, необходимо защитить хост-компьютеры (или виртуальные машины) от доступа злоумышленников. Безопасные настройки сети, использование брандмауэра, а также управление идентификацией и доступом для нативного облачного развертывания ничуть не менее релевантны, чем для обычного.
  • Контейнеры обычно работают под управлением механизма координации — в современных развертываниях его роль обычно играет Kubernetes, хотя есть и другие варианты, например Docker Swarm и Hashicorp Nomad. Недостаточная безопасность настроек средства координации или отсутствие должного контроля над доступом с правами администратора открывают злоумышленникам дополнительные векторы атак.

Больше информации о моделях угроз при развертываниях на основе Kubernetes можно найти в отчете «Модель угроз Kubernetes» (Kubernetes Threat Model) (https://bit.ly/3sg7aBn), заказанном CNCF.

Кроме того, команда Financial User Group проекта CNCF опубликовала дерево атак Kubernetes (Kubernetes Attack Tree) (https://bit.ly/3mPGZR2), созданное на основе методологии STRIDE (https://oreil.ly/rNmPN).

Границы зон безопасности

Границы зон безопасности (иногда называемые границами доверия) между частями системы означают, что наборы прав доступа в этих частях отличаются. Иногда границы задаются в ходе администрирования — например, в системах под управлением Linux системный администратор может модифицировать границы зон безопасности, указывая, какие группы файлов доступны пользователю. Администратор делает это, изменяя группы, в которых состоит данный пользователь. Если вы подзабыли систему прав доступа к файлам Linux, то мы напомним ее в главе 2.

Контейнер представляет собой зону безопасности. Код приложения должен работать внутри него и не иметь доступа к коду или данным вне контейнера, за исключением случаев, когда явным образом получает подобное разрешение (например, путем подключения к нему внешнего тома).

Чем строже граница зон безопасности между злоумышленником и его целью (например, данными пользователей), тем сложнее ему достичь этой цели.

Векторы атак, описанные в разделе «Модель угроз для контейнеров» на с. 23, можно связывать цепочкой — чтобы проникнуть через несколько границ зон безопасности. Приведу следующие примеры.

  • Злоумышленник может обнаружить, что уязвимость в одной из зависимостей приложения позволяет ему выполнять код удаленно, внутри контейнера.
  • Допустим, у взломанного компьютера нет прямого доступа к каким-либо ценным данным. Злоумышленнику необходим способ выйти за рамки контейнера, в другой контейнер или на хост-компьютер. Один из путей наружу — уязвимость выхода за рамки контейнера; другой — небезопасная конфигурация контейнера. Если один из этих путей открыт для злоумышленника, то он может получить доступ к хосту.
  • Следующий шаг — поиск способов получить на хосте полномочия суперпользователя, что может оказаться тривиальной задачей, если код приложения выполняется от имени суперпользователя внутри контейнера, как вы увидите в главе 4.
  • Имея полномочия суперпользователя на хост-компьютере, злоумышленник может получить доступ ко всему, что доступно с хоста или из любого контейнера, запущенного на этом хосте.

Добавление и укрепление границ безопасности при развертывании значительно усложняет жизнь взломщиков.

Важный аспект модели угроз — учет возможности атак изнутри среды, в которой работает приложение. При развертывании в облаке отдельные ресурсы порой задействуются совместно с другими пользователями и их приложениями. Совместное использование ресурсов компьютера называется мультиарендностью (multitenancy) и серьезно влияет на модель угроз.

Мультиарендность

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

Мультиарендность — идея, появившаяся еще во времена мейнфреймов в 1960-х, когда пользователи арендовали время CPU, память и место на диске на совместно используемой ими машине. Эта концепция не так уж сильно отличается от современных общедоступных облачных сервисов, например Amazon AWS, Microsoft Azure и Google Cloud Platform, в которых пользователи арендуют время CPU, оперативную память, место для хранения наряду с прочими возможностями и управляемыми сервисами. С тех пор как в 2006 году Amazon AWS предоставил EC2, можно арендовать экземпляры виртуальных машин, запущенные на стойках серверов в центрах обработки данных, разбросанных по всему миру. На одной реальной машине может работать несколько виртуальных (VM), и работающий на группе виртуальных машин пользователь может не знать, кто работает на соседней VM.

Совместно используемые машины

В некоторых случаях одну (возможно, виртуальную) машину Linux задействуют несколько пользователей. Так, очень часто подобный пример истинной мультиарендности встречается в университетах, где пользователи не доверяют друг другу и, говоря откровенно, администраторы не доверяют пользователям. В такой среде доступ пользователей ограничивается благодаря средствам управления доступом Linux. У каждого пользователя есть свой идентификатор входа, и доступ ограничивается с помощью средств управления доступом Linux, чтобы, например, пользователь мог изменять только файлы в своих каталогах. Можете представить, какой бы возник беспорядок, если бы студенты могли читать или — хуже того — редактировать файлы своих однокурсников?

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

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

Вообще говоря, считается, что виртуальные машины достаточно надежно изолированы друг от друга, то есть маловероятно, что ваши соседи смогут увидеть или вмешаться в происходящее в ваших виртуальных машинах. Как достигается подобная изоляция, можно узнать в главе 5. На самом же деле в соответствии с общепринятым определением (https://oreil.ly/yfkQI) виртуализация вообще не является мультиарендностью. Мультиарендность — это когда различные группы пользователей совместно работают с одним экземпляром программного обеспечения, а при виртуализации у пользователей нет доступа к гипервизору, управляющему их виртуальными машинами, поэтому они не используют совместно никаких программ.

Впрочем, изоляция виртуальных машин отнюдь не идеальна, и пользователи ранее жаловались на проблемы с «шумными соседями», когда совместное использование реальной машины может привести к неожиданным колебаниям производительности. Компания Netflix одной из первых начала применять общедоступные облачные сервисы. В 2010 году они опубликовали в своем блоге сообщение, в разделе «Совместная аренда — вещь непростая» (Co-tenancy is hard) (https://oreil.ly/CGlZ0) которого признаются, что создаваемые ими системы могли осознанно прекратить выполнение подзадачи, если она выполнялась слишком медленно. В последнее время можно услышать утверждения, что проблема «шумных соседей» больше не актуальна (https://oreil.ly/iE4qE).

Известны случаи, когда уязвимости в программном обеспечении приводили к нарушению границ между виртуальными машинами.

Возможные последствия несанкционированного доступа для некоторых приложений и организаций (особенно правительственных, финансовых и медицинских) настолько серьезны, что оправданно полное физическое разделение. Чтобы гарантировать полную изоляцию рабочих заданий, можно использовать частное облако, работающее в вашем собственном центре обработки данных либо под управлением выбранного вами поставщика сервисов. В частных облаках иногда встречаются дополнительные меры защиты, например дополнительные проверки анкетных данных персонала, имеющего доступ к ЦОД.

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

Мультиарендность контейнеров

В главе 4 вы увидите, что изоляция контейнеров не так строга, как изоляция виртуальных машин. Хотя это зависит от профиля рисков, вряд ли имеет смысл использовать контейнеры на одной машине в качестве не доверенных сторон.

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

В Kubernetes с помощью пространств имен (namespaces) кластер компьютеров можно разбить на части, используемые различными людьми, командами или приложениями.

«Пространство имен» — термин с несколькими значениями. В Kubernetes пространство имен представляет собой высокоуровневую абстракцию, которая предназначена для разбиения ресурсов кластера с возможным применением к ним различных режимов управления доступом. В Linux пространство имен — низкоуровневый механизм изоляции ресурсов машины, доступных процессу. Более подробно эти пространства имен будут описаны в главе 4.
Используйте механизмы управления доступом на основе ролей (role-based access control, RBAC), чтобы указать, какие пользователи и компоненты могут обращаться к тем или иным пространствам имен Kubernetes. Подробная инструкция по выполнению этого выходит за рамки данной книги. Хотелось бы лишь упомянуть, что RBAC Kubernetes позволяет контролировать только действия, производимые через API Kubernetes. Контейнеры приложений в модулях Kubernetes, работающих на одном хосте, защищены друг от друга лишь с помощью изоляции контейнеров, как описано в данной книге, даже если находятся в разных пространствах имен. Если злоумышленнику удастся выйти за рамки контейнера на хост, то границы пространств имен Kubernetes ни на йоту не изменят его возможностей влиять на другие контейнеры.

Экземпляры контейнеров

Такие облачные сервисы, как Amazon AWS, Microsoft Azure и Google Cloud Platform, предоставляют множество управляемых сервисов (managed services), с помощью которых пользователи могут арендовать программное обеспечение, хранилище и другие компоненты, не прибегая к их установке или управлению ими. Классический пример — сервис реляционных баз данных (Relational Database Service, RDS) Amazon; он позволяет легко подготовить к работе базы данных, использующие такое популярное ПО, как PostgreSQL, а для резервного копирования данных достаточно поставить нужную галочку (и оплатить счет, конечно).
Управляемые сервисы распространились и на мир контейнеров. С помощью сервисов Azure Container Instances и AWS Fargate можно запускать контейнеры, не задумываясь о том, на каком компьютере (или виртуальной машине) они будут работать.

Это снимает с наших плеч немалый груз администрирования и позволяет легко масштабировать развертываемую систему. Однако (по крайней мере теоретически) экземпляры контейнеров разных пользователей можно располагать на одной виртуальной машине. Уточните у вашего поставщика облачных сервисов на всякий случай.

Теперь вам известно немало угроз, потенциально опасных для развертываемых вами систем. Прежде чем перейти к оставшейся части книги, я бы хотела познакомить вас с основными принципами безопасности, которые пригодятся вам при выборе утилит и процессов безопасности, требуемых при развертывании.

Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок

Для Хаброжителей скидка 25% по купону — Райс

По факту оплаты бумажной версии книги на e-mail высылается электронная книга.

Комментарии (0)