Как читать авторский курс и нормативные материалы
Это учебные материалы, а не заключение о соответствии конкретного продукта. Организационные модели курса отделяйте от терминов стандарта: ППК в опубликованном проекте — машиночитаемый перечень, а живой реестр его версий является практикой сопровождения. Термин OSA в разных материалах имеет разный охват. Статус и обязательность нормативного документа проверяются отдельно; проект КАПО опубликован как проект первой редакции.
containerized build
Почему это важно для SCA/OSA
Сборка C/C++ в контейнере с multi-stage Dockerfile создаёт характерную ловушку для SCA: то, что формировало бинарь, и то, что попадает в финальный образ, — это разные множества. В стадии сборки ставятся -dev-пакеты (libssl-dev, zlib1g-dev, заголовки, статические .a), компилятор, кодогенераторы. В рантайм-образ через COPY --from=build переезжает только готовый бинарь и, может быть, рантайм-.so. Если генерировать SBOM по финальному образу, всё, что было в build-стадии, исчезает — а именно оно определило версии слинкованного кода.
Особенно остро это со статической линковкой: libfoo-dev дал .a, код вкомпилировался в бинарь, а сам пакет в финальном образе отсутствует. Скан рантайм-образа покажет «чистый» бинарь без зависимости, которая физически в нём есть.
Как это проявляется
Классический multi-stage Dockerfile прячет состав на стыке стадий:
FROM debian:12 AS build
RUN apt-get update && apt-get install -y libssl-dev zlib1g-dev g++ cmake # сформировали бинарь
COPY . /src
RUN cmake -S /src -B /b && cmake --build /b
FROM debian:12-slim # чистый рантайм
COPY --from=build /b/app /usr/bin/app # переехал только бинарь
# libssl-dev/zlib1g-dev здесь НЕТ — но их код в app
Снимите состав в build-стадии, а не в финале
# внутри build-стадии (target):
docker build --target build -t app:build .
docker run --rm app:build dpkg -l > build-stage-packages.txt # полный список -dev пакетов
# сравните с финалом:
docker run --rm app:final dpkg -l > runtime-packages.txt
build-stage-packages.txtсодержитlibssl-dev 3.0.13, которого нет вruntime-packages.txt, — это и есть пропавшая зависимость.
Слепая зона или приём
Слепая зона — SBOM рантайм-образа выдают за SBOM продукта. Build-стадия с её -dev-пакетами и статическими библиотеками не отражается в финальном образе, и поздний скан её не восстановит. Дополнительный риск — образ может содержать удалённые на вид артефакты (секреты, исходники) в нижних слоях, но для состава главное именно потеря build-стадии.
SBOM продукта собирается из build-стадии Снимайте инвентарь пакетов и команды компилятора в той стадии, где формировался бинарь (
--target build), и объединяйте с инвентарём рантайм-образа. SBOM только финального образа систематически недосчитывает того, что слинковано статически.
Что делать на проекте
- Стройте build-стадию отдельно (
docker build --target build) и снимайте в нейdpkg -l/rpm -qa— это состав сборочного окружения. - Снимите реальные команды компиляции/линковки внутри build-стадии (build-scan,
VERBOSE=1), чтобы поймать статические.a. - Сгенерируйте SBOM отдельно для build-стадии и для рантайм-образа; объедините, отметив, что слинковано статически.
- Зафиксируйте базовые образы с дайджестом (
debian:12@sha256:...), а не по тегу — иначе состав плывёт. - Прогоните
lddпо бинарю внутри финального образа: динамические зависимости видны, статические — нет, их добирайте из build-стадии. - Проверьте нижние слои образа на лишние артефакты сборки (см. смежные заметки про слои и обфускацию).
Связь
- Модуль: М09. C/C++ legacy
- Практики: П37. Контейнерный образ слои секреты и удаленные файлы · П38. Честный SBOM контейнерного образа · П19. C C++ make build inventory
- Нормативка: ГОСТ Р 56939-2024 · BSI TR-03183-2 v2.1.0 · CISA Minimum Elements for SBOM
- Смежные заметки: container image layers · container image obfuscation · honest SBOM · статическая линковка