Пиков / КАПО
Как читать авторский курс и нормативные материалы

Это учебные материалы, а не заключение о соответствии конкретного продукта. Организационные модели курса отделяйте от терминов стандарта: ППК в опубликованном проекте — машиночитаемый перечень, а живой реестр его версий является практикой сопровождения. Термин OSA в разных материалах имеет разный охват. Статус и обязательность нормативного документа проверяются отдельно; проект КАПО опубликован как проект первой редакции.

П37. Контейнерный образ: слои, секреты, удалённые файлы

Цель

Доказать на собственноручно собранном образе, что файл, удалённый инструкцией RUN rm, физически остаётся в нижнем слое и извлекается из tar-архива. Проверяемый результат — извлечённое из слоя содержимое файла, которого нет в финальной файловой системе контейнера.

Почему так происходит Каждая инструкция Dockerfile создаёт отдельный слой (overlay). RUN rm key.api не стирает байты из предыдущего слоя — он лишь добавляет в новый слой whiteout-маркер .wh.key.api, который прячет файл при сборке union-FS. Исходные байты остаются в blob нижнего слоя и видны любому, кто распакует образ. Multi-stage build с COPY --from — единственный надёжный способ не тащить секрет дальше.

Команда или действие

Соберите учебный образ с «секретом», который потом якобы удаляется:

mkdir layers-demo && cd layers-demo

# 1. фейковый секрет (НЕ настоящий ключ)
printf -- '-----BEGIN OPENSSH PRIVATE KEY-----\nFAKEKEYdoNotUse123456789\n-----END OPENSSH PRIVATE KEY-----\n' > key.api

# 2. Dockerfile, который «прячет» секрет в нижнем слое
cat > Dockerfile <<'EOF'
FROM busybox:latest
COPY key.api .
RUN rm key.api
EOF

docker build -t secrets-test .

# 3. в финальной ФС файла нет — RUN rm «сработал»
docker run --rm secrets-test ls -la /key.api   # No such file or directory

# 4. но он лежит в слое: распаковываем образ и ищем секрет
docker save secrets-test:latest | tar -x -C .
find blobs/sha256/ -type f -exec sh -c 'tar -tf "$1" 2>/dev/null | grep -q "^key.api$" && echo "FOUND in $1"' _ {} \;

Дополнительно — два инструмента, которые делают это автоматически:

# послойный аудит: dive показывает, что добавил/удалил каждый слой
dive secrets-test:latest

# поиск секретов сразу по всем слоям образа
trivy image --scanners secret secrets-test:latest

Что вы должны увидеть

В пункте 4 find выводит blob, внутри которого лежит «удалённый» файл, а распаковка показывает реальный секрет:

FOUND in blobs/sha256/b00389...f17b98
$ tar xvf blobs/sha256/b00389...f17b98 key.api && cat key.api
-----BEGIN OPENSSH PRIVATE KEY-----
FAKEKEYdoNotUse123456789
-----END OPENSSH PRIVATE KEY-----

trivy image --scanners secret находит не меньше 1 секрета и помечает слой, в котором он лежит:

secrets-test (busybox 1.x)
Total: 1 (HIGH: 1)
key.api  AsymmetricPrivateKey  HIGH  AsymmetricPrivateKey

В dive слой с RUN rm key.api отображается с положительным «вкладом» (добавлен whiteout), а не как удаление байтов — на левой панели видно, что размер образа от удаления не уменьшился.

Если не получилось

Критерий зачёта

Покажите преподавателю: (1) вывод docker run ... ls, подтверждающий, что в финальной ФС файла нет; (2) содержимое key.api, извлечённое из blob'а слоя; (3) находку trivy --scanners secret с указанием слоя. Сформулируйте письменно, почему RUN rm не является способом удалить секрет и какой приём (multi-stage / --mount=type=secret) решает задачу.

Не превращайте демо в реальную утечку Используйте только фейковый ключ. Реальный секрет, попавший в слой и уехавший в реестр, считается скомпрометированным с момента push — ротация обязательна, «удаление тега» не помогает.

Нормативная связка