10 практических рекомендаций по безопасности образов Docker. Часть 2

Автор оригинала: LIRAN TAL, OMER LEVI HEVRONI
  • Перевод
Перевод статьи подготовлен специально для студентов курса «Безопасность Linux».


Читать первую часть


5. Не оставляйте чувствительные данные в образах Docker


Иногда при создании приложения внутри образа Docker вам нужны такие секретные данные, как приватный SSH-ключ для извлечения кода из приватного репозитория или токены для установки закрытых пакетов. Если вы копируете их в промежуточный контейнер Docker, они кэшируются в том слое, к которому они были добавлены, даже если вы удалите их позже. Эти токены и ключи должны храниться вне Dockerfile.

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


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

FROM: ubuntu as intermediate

WORKDIR /app
COPY secret/key /tmp/
RUN scp -i /tmp/key build@acme/files .

FROM ubuntu
WORKDIR /app
COPY --from=intermediate /app .

Используйте команды Docker Secrets


Используйте альфа-функцию в Docker для управления секретными данными для монтирования конфиденциальных файлов без их кэширования:

# syntax = docker/dockerfile:1.0-experimental
FROM alpine

# shows secret from default secret location
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecre

# shows secret from custom secret location
RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar

Вы можете узнать больше об управлении секретными данными на сайте Docker.

Остерегайтесь рекурсивного копирования


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

COPY . .

Если в вашей папке есть конфиденциальные файлы, удалите их или используйте .dockerignore, чтобы игнорировать их:

private.key
appsettings.json

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


Каждый образ Docker может иметь несколько тегов, которые представляют варианты одних и тех же образов. Самый распространенный тег — latest, представляющий последнюю версию образа. Теги образов не являются иммутабельными, и автор образов может публиковать один и тот же тег несколько раз.

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

Есть несколько способов устранить эту проблему:

  • Предпочитайте наиболее конкретный тег из доступных. Если образ имеет несколько тегов, таких как :8 и :8.0.1 или даже :8.0.1-alpine, предпочтите последний, так как он является наиболее конкретной ссылкой на образ. Избегайте использования наиболее общих тегов, например, latest. При закреплении конкретного тега учитывайте, что он может быть в конечном итоге удален.
  • Чтобы устранить проблему, связанную с тем, что определенный тег образа становится недоступным и становится ограничителем показа для групп, которые полагаются на него, рассмотрите возможность запуска локального зеркала этого образа в реестре или учетной записи, которая находится под вашим собственным контролем. Важно принимать во внимание затраты на обслуживание, необходимые для этого подхода, поскольку это означает, что вам необходимо поддерживать реестр. Хорошей практикой является тиражирование образа, который вы хотите использовать в вашем реестре, чтобы убедиться, что образ, который вы используете, не изменяется.
  • Будьте предельно конкретны! Вместо того, чтобы подтягивать тег, подтяните образ, используя конкретную SHA256-ссылку на образ Docker, что гарантирует, что вы получите один и тот же образ для каждого запроса. Однако обратите внимание, что использование SHA256-ссылки может имеет следующий риск — если изменится образ, то хэш может больше не работать.

7. Используйте COPY вместо ADD


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

  • COPY — рекурсивно копирует локальные файлы, с указанием исходных и целевых файлов или каталогов. С COPY вы должны объявлять локации.
  • ADD — рекурсивно копирует локальные файлы, неявно создает каталог назначения, если он не существует, и принимает архивы как локальные или удаленные URL-адреса в качестве источника, который он расширяет или загружает соответственно в каталог назначения.
    Хотя различия между ADD и COPY не такие уж фундаментальные, они важны. Помните о них, чтобы избежать потенциальных проблем безопасности:
  • Когда для загрузки данных непосредственно в исходное местоположение используются удаленные URL-адреса, это может привести к атакам посредника, которые изменяют содержимое загружаемого файла. Кроме того, происхождение и подлинность удаленных URL-адресов должны быть дополнительно проверены. При использовании COPY источник для файлов, которые будут загружены с удаленных URL-адресов, должен быть объявлен через безопасное TLS-соединение, и их происхождение также должно быть проверено.
  • Замечания о пространстве и слоях образов: использование COPY позволяет отделить добавление архива от удаленных локаций и распаковать его в виде разных слоев, что оптимизирует кэш образов. Если требуются удаленные файлы, объединение их всех в одну команду RUN, которая впоследствии загружает, извлекает и очищает, оптимизируя однослойную операцию на нескольких слоях, которые потребовались бы при использовании ADD.
  • Когда используются локальные архивы, ADD автоматически извлекает их в каталог назначения. Хотя это может быть приемлемым, это добавляет риск получения zip-бомб и Zip Slip уязвимостей, которые затем могут запускаться автоматически.

8. Используйте метки метаданных


Метки образов предоставляют метаданные для образов, которое вы создаете. Это помогает пользователям легче разобраться как использовать образ. Самой распространенной меткой является «maintainer», которая указывает адрес электронной почты и имя человека, поддерживающего этот образ. Добавляйте метаданные с помощью следующей команды LABEL:

LABEL maintainer="me@acme.com"

В дополнение к контактам сопровождающего добавляйте любые метаданные, которые важны для вас. Эти метаданные могут содержать: хеш коммита, ссылку на соответствующую сборку, статус качества (все ли тесты пройдены?), исходный код, ссылку на местоположение файла SECURITY.TXT и т. д.

Хорошей практикой является поддержка файла SECURITY.TXT (RFC5785), который указывает на вашу Responsible Disclosure политику для вашей схемы меток Docker при добавлении новых, например:

LABEL securitytxt="https://www.example.com/.well-known/security.txt"

Смотрите дополнительную информацию о метках для образов Docker:

https://label-schema.org/rc1/

9. Используйте многоэтапную сборку для небольших и безопасных образов


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

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

Golang является отличным примером. Чтобы создать приложение Golang, вам нужен компилятор Go. Компилятор создает исполняемый файл, который работает в любой операционной системе, без зависимостей, включая чистые образы.

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

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

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


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

Одним из таких линтеров является hadolint. Он анализирует Dockerfile и выдает предупреждение о любых ошибках, которые не соответствуют его рекомендациям.



Hadolint становится еще более мощным, когда используется в интегрированной среде разработки (IDE). Например, при использовании hadolint в качестве расширения VSCode при вводе появляются ошибки линтинга. Это помогает в написании лучших Dockerfiles быстрее.

Узнайте больше о защите ваших образов Docker
OTUS. Онлайн-образование
Цифровые навыки от ведущих экспертов

Комментарии 4

    0

    ..del
    опс. поторопился… недочитал.

      0
      Используйте COPY вместо ADD

      Это разные команды. И правильно говорить «используйте COPY для копрования файлов и ADD для распаковки архивов», а не использовать вместо.
      В общем этот совет крайне странный, особенно если добавляешь свой дистрибутив, созданый ранее в maven/gradle в виде zip или tar.gz
        0
        Ничуть не странный. Постоянно встречаю Dockerfile, в которых одиночные файлы добавляются при помощи ADD вместо COPY.
          0
          А например архивы с артифактори тоже через COPY в докер добавлять и распаковывать?

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое