Исчерпывающее пособие по безопасной сборке Docker-образов

Исчерпывающее пособие по безопасной сборке Docker-образов

Рассказываем, как настроить сборку образов Docker, обеспечить безопасность и добавить сканирование в пайплайн.

Изображение записи

Каждый контейнер Docker основан на образе, который обеспечивает основу для всего, что вы когда-либо будете развертывать и запускать. Если злоумышленник каким-то образом повлияет на сборку образа и изменит Dockerfile, то сможет совершить вредоносные действия, например:

  • добавить вредоносный код, который запустится в продакшене и в дальнейшем проникнет в среду эксплуатации; 
  • получить доступ к секретным данным сборки;
  • получить полную информацию о топологии сети, доступной из инфраструктуры сборки;
  • атаковать хост-компьютер, на котором производится сборка.

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

Векторы атак на образы.

Далее на основе инструкций в Dockerfile рассмотрим рекомендации по сборке образа.

Используйте минимальный базовый образ

Для запуска контейнера можно либо скачать уже готовый публичный образ, либо создать свой на базе публичного. Остановимся пока на втором. Если вы уже знакомы с Docker, то должны были сталкиваться с идеей создания собственного образа на основе инструкций из Dockerfile, в котором описаны все шаги. 

Образ контейнера состоит из двух частей: корневой файловой системы и конфигурации. Другими словами, он включает в себя базовый образ и команды, которые будут изменять содержимое файловой системы — например, ADD, COPY, RUN — и хранить информацию о настройках, USER, PORT и ENV.

Первая строка Dockerfile — это инструкция FROM, в которой задается базовый образ для создания нового. Выбор базового образа сильно влияет на итоговый, особенно на его размер. Если проекту не нужны общие системные библиотеки или системные утилиты, лучше не использовать полнофункциональную ОС в качестве базового образа. 

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

Преимущество меньших образов также заключается и в более быстрой передаче по сети. Например, если образ Alpine занимает 5,6 МБ, а образ Ubuntu — 72,8 МБ, то первый должен закачиваться быстрее. Сравним на примере. 

1. Создадим простой Dockerfile c одной инструкцией и соберем свой образ на основе базового образа Python:


    FROM python
$ docker build . -f python -t python

2. Далее оценим размер итогового образа:


    $ docker images --format "{{.Repository}}: {{.Size}}" | grep python
python: 925MB

3. Затем создадим образ на основе легковесного Alpine и установим все необходимое для работы Python:


    FROM alpine

RUN apk add --update-cache
RUN apk add python3
RUN apk add python3-dev
RUN apk add py-pip
RUN apk add build-base
RUN pip install virtualenv 
# удаление кэша
RUN rm -rf /var/cache/apk/*

4. Наконец, сравним размер полученных образов:


    $ docker images --format "{{.Repository}}: {{.Size}}"
alpine-python: 392MB

Образ на основе Alpine в 2 раза легче! Конечно, образ на основе Python удобнее в использовании, потому что предоставляет довольно много предустановленных библиотек ОС и пакетов разработчика. Но готовы ли вы отслеживать все содержимое этого образа и исправлять уязвимости? Чем больше содержимое, тем больше уязвимостей. 

Оптимизируйте инструкции в Dockerfile

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

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

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

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

Используйте фиксированные теги. Если не указать тег, то по умолчанию будет использоваться latest:


    FROM node = FROM node:latest

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


    FROM node:17.0.1

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

Объединяйте команды и управляйте кэшем. Некоторые команды, как RUN, COPY или FROM, порождают новый слой, который занимает место на локальном диске. Мы можем объединить команды, установив сразу оба пакета, как в примере ниже:


    FROM alpine:3.19

RUN apk cache clean
RUN apk update
RUN apk add python3

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


    # docker inspect 2127cf94ff15
…
"RootFS": {
        	"Type": "layers",
        	"Layers": [
            	"sha256:d4fc045c9e3a848011de66f34b81f052d4f2c15a17bb196d637e526349601820",            	"sha256:b31d2d0c4fa1bdf747c1f5c767bbe3b56ece3aedf13ee159a9fb2313a7148561",            	"sha256:04f1d9daf0984268759b423cbf3fd4ab5bf5210ef4348f950b8fc5d9ca4c6771",           	"sha256:37ea94294a9f18eaf557a2a53eecac592630868800e8d1e11972e25a3a8c25e0"
        	]
},

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


    FROM alpine:3.19

RUN apk cache clean \
apk update \
apk add python3

    # docker inspect 6207a1cbac1b
…
"RootFS": {
        	"Type": "layers",
        	"Layers": [
            	"sha256:d4fc045c9e3a848011de66f34b81f052d4f2c15a17bb196d637e526349601820",            "sha256:5fab68584e95d457aed76251b6f66990157c78ccf02d19bdef3d32f40ade0e7c"
        	]
    	}, 

Чистите кэш пакетного менеджера — он занимает пространство на диске. Здесь все просто:


    FROM alpine:3.19

RUN apk cache clean \
apk update \
apk add python3 \
rm -rf /var/cache/apk/

Удаляйте файлы в той же инструкции, в которой они создаются. Если сделать это в следующей инструкции, он останется в предыдущем слое и никуда не денется с диска.

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

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

Используйте инструкцию COPY вместо ADD. Команда COPY рекурсивно копирует локальные файлы с заданными исходными и целевыми файлами или каталогами. Команда ADD, в свою очередь, рекурсивно копирует локальные файлы, неявно создает каталог назначения, а также работает с локальными или удаленными архивами. То есть может скачать и распаковать архив. 

Автоматическая распаковка может привести к эксплуатации атаки — например, ZIP bomb. Злоумышленник способен подменить архив и запустить зловредный код внутри контейнера.

Не выполняйте контейнеры от имени суперпользователем

Как ранее было сказано, Docker-контейнеры запускаются от root-пользователя по умолчанию. Зачастую поставляемые готовые образы настроены так, что выполнение происходит от суперпользователя. Но ситуаций, когда это действительно необходимо, крайне мало. 

Например, для открытия портов с номером меньше 1024 необходима привилегия CAP_NET_BIND_SERVICE. Она есть при выполнении от суперпользователя, но в контейнере в лишена смысла, так как можно настроить проброс портов. 

Кроме того, часто во время выполнения контейнеров запускают root, чтобы установить ПО с помощью пакетных менеджеров. На практике это логичнее делать во время сборки образа, а после — использовать non-root. Иначе нерационально, так как новый софт не сканируется на уязвимости, а контейнер перестает быть неизменяемым.

Таким образом, везде, где есть возможность, лучше избегать выполнения от суперпользователя или «маппить» его, чтобы внутри контейнера отличался от root хоста. Поэтому рекомендую создавать в Dockerfile отдельную группу и пользователя и выделять ему минимально необходимые привилегии:


    # создать группу и пользователя
RUN groupadd -r tom && useradd -g tom tom
# определить права и владельцев 
RUN chown -R tom:tom /my-app
# переключиться на пользователя 
USER tom

Стоит отметить, что у некоторых базовых образов уже есть общие пользователи, которые можно использовать в своем образе. Например, NodeJS уже имеет non-root пользователя node. Но это все теория — рассмотрим создание образа от root на примере MariaDB.

1. Скачаем образ и запустим контейнер согласно официальной инструкции:


    # docker pull mariadb
Using default tag: latest
latest: Pulling from library/mariadb
10ac4908093d: Pull complete
44779101e748: Pull complete
a721db3e3f3d: Pull complete
1850a929b84a: Pull complete
504a6a98ce6b: Pull complete
c8f425c678e4: Pull complete
b698a9bf4d23: Pull complete
0557d74d39ca: Pull complete
Digest: sha256:5cd42a3ca1718c6e6d1908c15d3237e77dee5a52c42c822f833247b6d501f486
Status: Downloaded newer image for mariadb:latest
docker.io/library/mariadb:latest
# docker run -p 127.0.0.1:3306:3306  --name mariadb -e MARIADB_ROOT_PASSWORD=superpass -d mariadb
d617a77532919cfb16a68bb4607607192cb61fdb45473a80319ea1a5f3ad51a1
# docker ps
CONTAINER ID   IMAGE 	COMMAND              	CREATED          	STATUS          	PORTS                  	NAMES
d617a7753291   mariadb   "docker-entrypoint.s…"   About a minute ago   Up About a minute   127.0.0.1:3306->3306/tcp   mariadb

2. Затем попробуем перейти «внутрь» контейнера в интерактивном режиме, проверить пользователя и его возможности:


    # docker exec -ti mariadb sh
# whoami
root
# cd /root/
# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
	link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
	link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0

Видим, что по умолчанию контейнер запустился с суперпользователем. Ему доступен root-каталог и даже просмотр информации об интерфейсах. 

3. Попробуем удалить интерфейс:


    # ip link delete eth0
RTNETLINK answers: Operation not permitted

Видим, что root-пользователю это действие недоступно.

Исключите утечки чувствительной информации в образы

Иногда при создании приложения нужны секреты — например, ключи SSH, токены и прочее. Но если эту информацию «захардкодить» в Dockerfile, то она будет закэширована и доступна остальным пользователям. Поэтому все чувствительные данные следует хранить за пределами Dockerfile. И не забываем про автоматизированные средства по поиску секретов.

Используйте хранилища образов

Собранный образ обычно сохраняют в реестре — скорее всего, вам уже приходилось работать с публичным Docker Hub и GitLab container registry. Но кроме него можно использовать и другие специальные облачные сервисы. 

Хранить собственные образы, особенно в случае enterprise, нужно в частных репозиториях. Его можно поднять самостоятельно или воспользоваться уже готовым сервисом — например, Container Registry от Selectel.

Используйте надежные источники

Что, если понадобится сторонний образ контейнера в качестве приложения или образа для сборки? Можно взять подписанный образ от поставщика ПО или из другого доверенного источника. Единственное, что возможно понадобится, — протестировать его образ перед сохранением в своем реестре.

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

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

  • проверить, используются ли свежие обновления ОС и как часто обновляется образ;
  • узнать, был ли образ просканирован на наличие уязвимостей и вредоносного ПО;
  • оценить, стоит ли доверять автору и является ли образ официальным;
  • проверить, выполняется ли образ от имени суперпользователя;
  • проверить, является ли образ подписанным;
  • выяснить, является ли данный источник доверенным, сходятся ли контрольные суммы образа.

Подписывайте и проверяйте образы

Перед тем, как «пулить» базовый образ, можно проверить его на наличие подписи. Так мы сможем определить, является ли образ официальным. Для этого можно включить Docker Content Trust в конфигурации Docker:


    export DOCKER_CONTENT_TRUST=1

Эта команда защитит от использования неподписанных образов. Если попытаться их «запулить», то запрос будет отклонен. Подписывать свои образы можно с помощью инструмента Docker Notary.

Используйте статический анализ образов

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

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

Помните, что уязвимость в любом компоненте Dockerfile будет присутствовать во всех контейнерах, которые вы создадите на его основе. Поэтому важно внедрить сканирование образов на наличие уязвимостей. Оно должно происходить постоянно, после каждого изменения. Должно быть встроено в CI/CD, а также может быть настроено для автоматического сканирования каждого нового образа в реестре.

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

Кроме того, сканеры могут сканировать не только файлы внутри Dockerfile, но и его метаданные. Это позволяет найти проблемы в конфигурации образов контейнеров. 

Пример сканирования уязвимостей

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

В основе сканирования контейнеров используются опубликованные общественные базы данных уязвимостей — Common Vulnerability and Exposure (CVE). Каждой угрозе присваивается свой уникальный номер — например, CVE-2023-52136 — и этот список постоянно пополняется новыми записями. Кроме того, есть и другие базы уязвимостей, на которых открыто размещается информация. Среди них — CVE и VULDB.  

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

Для сканирования можно воспользоваться встроенным в Docker сканером, базирующимся на Snyk. Он анализирует содержимое и зависимости проекта, сверяя их со своей базой уязвимостей. Ниже представлен пример вывода docker scan:


     $ docker scan ubuntu
 
Testing ubuntu...
 
✗ Low severity vulnerability found in shadow/passwd
  Description: Time-of-check Time-of-use (TOCTOU)
  Info: https://snyk.io/vuln/SNYK-UBUNTU2004-SHADOW-577863
  Introduced through: shadow/passwd@1:4.8.1-1ubuntu5.20.04.1, adduser@3.118ubuntu2, shadow/login@1:4.8.1-1ubuntu5.20.04.1, util-linux/mount@2.34-0.1ubuntu9.1
  From: shadow/passwd@1:4.8.1-1ubuntu5.20.04.1
  From: adduser@3.118ubuntu2 > shadow/passwd@1:4.8.1-1ubuntu5.20.04.1
  From: shadow/login@1:4.8.1-1ubuntu5.20.04.1
  and 1 more...
 
✗ Low severity vulnerability found in pcre3/libpcre3
  Description: Uncontrolled Recursion
  Info: https://snyk.io/vuln/SNYK-UBUNTU2004-PCRE3-580031
  Introduced through: pcre3/libpcre3@2:8.39-12build1, grep@3.4-1
  From: pcre3/libpcre3@2:8.39-12build1
  From: grep@3.4-1 > pcre3/libpcre3@2:8.39-12build1
 …
Organization:  	my-org
Package manager:   deb
Project name:  	docker-image|ubuntu
Docker image:  	ubuntu
Platform:      	linux/amd64
Base image:    	ubuntu:20.04
Licenses:      	enabled
 
Tested 93 dependencies for known issues, found 18 issues.
 
Base Image	Vulnerabilities  Severity
ubuntu:20.04  18           	0 critical, 0 high, 3 medium, 15 low
 
Recommendations for base image upgrade:
 
Major upgrades
Base Image          	Vulnerabilities  Severity
ubuntu:impish-20211015  12           	0 critical, 0 high, 2 medium, 10 lo

Есть и множество других инструментов для сканирования, среди них — open-source и enterprise-решения. Чтобы ознакомиться с популярными решениями на текущий момент, можно изучить топ-10. Желательно использовать комплексное решение, которое может сканировать как операционную систему, так и конкретные библиотеки, работающие в контейнере, и их зависимости. Главное — убедитесь, что сканер поддерживает языки, используемые компонентами в образе.

В качестве примера рассмотрим еще один сканер — Trivy. Он прост в использовании, славится удобной и богатой документацией, а также большим функционалом для интеграции. 

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


    # trivy image --scanners vuln --severity CRITICAL wordpress
...
wordpress (debian 12.4)

Total: 3 (CRITICAL: 3)

┌────────────┬────────────────┬──────────┬──────────────┬───────────────────┬───────────────┬────────────────────────────────────────────────────────┐
│  Library   │ Vulnerability  │ Severity │	Status	│ Installed Version │ Fixed Version │                     	Title                      	│
├────────────┼────────────────┼──────────┼──────────────┼───────────────────┼───────────────┼────────────────────────────────────────────────────────┤
│ libaom3	│ CVE-2023-6879  │ CRITICAL │ affected 	│ 3.6.0-1       	│           	│ aom: heap-buffer-overflow on frame size change     	│
│        	│            	│      	│          	│               	│           	│ https://avd.aquasec.com/nvd/cve-2023-6879          	│
├────────────┼────────────────┤      	├──────────────┼───────────────────┼───────────────┼────────────────────────────────────────────────────────┤
│ zlib1g 	│ CVE-2023-45853 │      	│ will_not_fix │ 1:1.2.13.dfsg-1   │           	│ zlib: integer overflow and resultant heap-based buffer │
│        	│            	│      	│          	│               	│           	│ overflow in zipOpenNewFileInZip4_6                 	│
│        	│            	│      	│          	│               	│           	│ https://avd.aquasec.com/nvd/cve-2023-45853         	│
├────────────┤            	│      	│          	│               	├───────────────┤                                                    	│
│ zlib1g-dev │            	│      	│          	│               	│           	│                                                    	│
│        	│            	│      	│          	│               	│           	│                                                    	│
│        	│            	│      	│          	│               	│           	│                                                    	│
└────────────┴────────────────┴──────────┴──────────────┴───────────────────┴───────────────┴────────────────────────────────────────────────────────┘

В отчете вывели только критические уязвимости для краткости, но на текущий момент хватает угроз уровня HIGH. Использовать такой образ весьма рискованно, особенно в enterprise.

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

  • Обновление базового образа. Если уязвимости связаны с базовым образом, проверьте наличие обновлений. Возьмите последнюю версию и пересоберите свой Dockerfile.
  • Обновление пакетов. Если определенные пакеты или библиотеки в образе затронуты уязвимостями, обновите их до исправных версий. Внесите необходимые изменения в Dockerfile или процесс сборки, чтобы обеспечить включение обновленных пакетов.

Дополнительные возможности сканеров

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

Сканирование ошибок конфигурации

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

Ошибки конфигурации могут быть связаны с открытыми портами, неправильными разрешениями файлов, недостаточными настройками безопасности, использованием паролей и имен пользователей по умолчанию. Сканер Trivy может обнаружить такие шероховатости, используя встроенные политики для сканирования известных IaC-файлов, Docker, Kubernetes, Terraform, CloudFormation и прочего. Кроме того, вы также можете написать собственные политики.

Для примера посмотрим, как с помощью Trivy сканировать образ на наличие misconfiguration (неверных конфигураций).


     #trivy image --scanners misconfig --severity HIGH,CRITICAL myapp                                                                    2024-02-15T13:19:26.131+0300    INFO    Misconfiguration scanning is enabled
2024-02-15T13:19:26.294+0300    INFO    Detected config files: 6

go/pkg/mod/github.com/!nerzal/gocloak/v13@v13.1.0/Dockerfile (dockerfile)

Tests: 19 (SUCCESSES: 18, FAILURES: 1, EXCEPTIONS: 0)
Failures: 1 (HIGH: 1, CRITICAL: 0)

HIGH: Specify at least 1 USER command in Dockerfile with non-root user as argument
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.

See https://avd.aquasec.com/misconfig/ds002
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


...

В отчете видим, что в образе используется root-пользователь, поэтому в Dockerfile нужно указать хотя бы одного non-root. Об опасностях данной уязвимости мы уже говорили выше.

Сканирование на наличие секретов

Сканирование образов на наличие секретов позволяет обнаружить, есть ли в них на конфиденциальная информация: пароли, ключи API, данные аутентификации и прочее. Если эти образы распространяются или становятся общедоступными, чувствительные данные могут быть раскрыты.

Вот, как работает сканирование на наличие секретов, на примере Trivy:


    
# trivy image --scanners secret --severity HIGH,CRITICAL myapp
2024-02-15T13:55:39.125+0300    INFO    Secret scanning is enabled
2024-02-15T13:55:39.125+0300    INFO    If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2024-02-15T13:55:39.125+0300    INFO    Please see also https://aquasecurity.github.io/trivy/v0.49/docs/scanner/secret/#recommendation for faster secret detection
/go/pkg/mod/github.com/lib/pq@v1.10.7/certs/server.key (secrets)

Total: 1 (HIGH: 1, CRITICAL: 0)

HIGH: AsymmetricPrivateKey (private-key)
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Asymmetric Private Key
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 /go/pkg/mod/github.com/lib/pq@v1.10.7/certs/server.key:1 (added by 'RUN /bin/sh -c go mod download && go bui')
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 [ -----BEGIN PRIVATE KEY-----*******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************-----END PRIVATE KEY-----
   2
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Сканирование на наличие вредоносов

Наконец, сканирование образов позволяет обнаружить вредоносное программное обеспечение или код. Это могут быть программы, цифровые вымогатели или другие типы вирусов, угрожающие вашей системе и данным. Для сканирования рекомендую использовать сервисы вроде Container scan от Kaspersky.

Используйте линтеры

Избежать уязвимостей можно еще на раннем этапе, если использовать линтеры — статические анализаторы кода. Хотя может показаться, что это не совсем про безопасность, они могут быть также полезны. 

  • Линтеры помогают выявлять потенциальные уязвимостей в коде: нежелательные паттерны и ненадежные функции. Это позволяет разработчикам предотвращать типичные проблемы безопасности до первого коммита.
  • Линтеры могут обнаружить нежелательное хранение паролей, некачественные обработчики ввода-вывода, уязвимости XSS (межсайтовый скриптинг) и другие типы уязвимостей.
  • Линтеры позволяют привести код к «одному виду», соблюдать установленные политики и стандарты. 

Линтеры чаще всего специфичны для конкретного языка. И даже для анализа Dockerfile есть специальные инструменты. Линтер способен проанализировать Dockerfile в реальном времени и подсветить предупреждения о любых несоответствиях стандартам. Пример такого инструмента — Hadolint. Подобный линтер может быть интегрирован в IDE и пайплайн.

Заключение

В рамках этой статьи мы обсудили не все аспекты. На очереди — безопасная работа с контейнерами. Тема объемная, поэтому подробности обсудим в следующем материале.