А поскольку бит setuid — опасный путь к повышению полномочий, некоторые анализаторы образов контейнеров сообщают о наличии файлов с установленным битом setuid. Можно также запретить его использование, прибегнув к флагу --no-new-privileges команды docker run. По умолчанию контейнеры запускаются от имени суперпользователя. Это значит, запущенные в контейнерах приложения с намного большей вероятностью, чем в обычной машине под управлением Linux, будут выполняться от имени суперпользователя. Злоумышленнику, захватившему контроль над процессом внутри контейнера, конечно, все равно придется найти способ выйти за рамки контейнера, но затем он сразу получит права суперпользователя на хосте, не прибегая к какому-либо дальнейшему повышению полномочий. Но даже если контейнер запущен не от имени root, все равно остается возможность повысить полномочия на основе механизмов прав доступа Linux: * образов контейнеров, содержащих исполняемые файлы с установленным битом setuid; * предоставления дополнительных привилегий контейнеру, запущенному от имени несуперпользователя. ### Создание контрольных групп Создание подкаталога внутри каталога группы memory приводит к созданию контрольной группы, а ядро Linux автоматически заполняет его различными файлами для параметров и статистики этой группы. При запуске контейнера среда выполнения создает для него новые контрольные группы. Утилита lscgroup (которую на Ubuntu можно установить с пакетом cgroup-tools) позволяет просмотреть эти контрольные группы на хост-компьютере. ### Установка ограничений на ресурсы Объем оперативной памяти, доступный контрольной группе для использования, можно узнать, заглянув в ее файл memory.limit_in_bytes ## Программные уязвимости в образах контейнеров ### Сканирование образов контейнеров Чтобы знать, не запущены ли в развернутой системе контейнеры с уязвимым программным обеспечением, необходимо просканировать в них все зависи- мости. Для этого существует несколько различных подходов. #### Неизменяемые контейнеры (Обычно) ничто не мешает контейнеру после запуска скачивать дополни- тельное программное обеспечение и размещать его в своей файловой системе. И действительно, на заре развития контейнеров подобный паттерн встречал- ся довольно часто, поскольку считался удобным способом обновления ПО контейнера до последних версий, который не нуждался в повторной сборке образа контейнера. Если до сих пор эта идея не приходила вам в голову, то забудьте ее сразу же, она считается исключительно неудачной по многим причинам, включая представленные ниже. ‰ * Если контейнер скачивает код во время выполнения, то в различных экземплярах контейнера могут работать разные версии этого кода, при- чем отследить, в каком экземпляре работает та или иная версия, будет непросто. В отсутствие сохраненной версии кода из данного контейнера будет сложно (или даже невозможно) воссоздать его идентичную копию, что создаст проблемы при попытках воспроизвести проблемы, возникшие при эксплуатации. ‰* Труднее контролировать и гарантировать происхождение программного обеспечения, работающего в каждом из контейнеров, если оно может скачиваться в любой момент времени и откуда угодно. ‰* Сборку образа контейнера и сохранение его в реестре можно с легкостью автоматизировать в конвейере CI/CD. Совсем нетрудно также добавить в тот же конвейер дополнительные проверки безопасности — например, сканирование на уязвимости или проверку цепочки поставок программ- ного обеспечения. Еще один способ реализовать неизменяемость — запустить контейнер с фай- ловой системой, предназначенной только для чтения. Если коду приложения требуется доступ к открытому для записи локальному хранилищу, то можно смонтировать открытую для записи временную файловую систему. Здесьможет понадобиться вносить изменения в приложение, чтобы оно записывало данные лишь в эту временную файловую систему. #### Средства сканирования Существует множество утилит для сканирования образов контейнеров, на- чиная от реализаций с открытым исходным кодом, таких как Trivy (https:// oreil.ly/SxKQT), Clair (https://oreil.ly/avK-2) и Anchore (https://oreil.ly/7rFFt), и до коммерческих решений от таких компаний, как JFrog, Palo Alto и Aqua. Во многих реестрах образов контейнеров, например Docker Trusted Registry (https://docs.docker.com/ee/dtr), и в проекте Harbor (https://goharbor.io/) от CNCF, а также в реестрах от основных поставщиков облачных сервисов есть встроенные возможности сканирования. ##### Количество мест, в которых можно ввести сканирование * Сканирование на стороне разработчика. Если используемый сканер можно легко развернуть на настольном компьютере, то отдельные разработчики могут сканировать локальные сборки образов на предмет проблем, благодаря чему получают шанс внести исправления до отправки исходного кода в репозиторий. ‰* Сканирование при сборке. Взвесьте возможность включения шага скани- рования сразу же после сборки образа контейнера в вашем конвейере. В случае обнаружения в результате этого уязвимостей, превышающих заданный порог серьезности, можно сообщить о неуспехе сборки, гарантируя тем самым, что она точно не будет развернута в промышленной эксплуатации. * Сканирование реестра. После того как образ будет собран, конвейер обыч- но помещает его в реестр образов. Имеет смысл регулярно сканировать образы на случай обнаружения новой уязвимости в одном из пакетов, используемых в давно не пересобиравшемся образе.