Удаление секретов сборки из образа

DockerBeginner
Практиковаться сейчас

Введение

Секреты сборки могут попасть в образы, если Dockerfile копирует слишком много файлов из контекста сборки. Распространенная ошибка — использование COPY . /app/: это удобно, но может привести к попаданию токенов, учетных данных, локальных заметок и временных файлов в итоговый образ. Как только секрет оказывается внутри слоя образа, средства защиты времени выполнения уже не могут надежно исправить эту ошибку в цепочке поставок.

В этой лабораторной работе вы создадите намеренно уязвимый локальный образ, докажете наличие в нем токена сборки, а затем исправите процесс сборки, сузив область копирования в Dockerfile и добавив .dockerignore. В завершение вы развернете чистый образ и убедитесь, что контейнер во время выполнения больше не содержит секрет сборки.

Изучение небезопасного контекста сборки

На этом этапе вы изучите контекст сборки и небезопасный Dockerfile. Контекст сборки — это директория, которая отправляется сборщику образа. Если Dockerfile копирует весь контекст, каждый файл в этой директории может стать частью итогового образа.

Начните работу в рабочей директории лаборатории:

cd /home/labex/project/build-secret-image

Прочитайте заметки:

sed -n '1,120p' BUILD_SECRET_NOTES.md

Выведите список файлов, которые в данный момент существуют в контексте сборки:

find . -maxdepth 1 -type f -printf '%f\n' | sort | tee evidence/initial-context-files.txt

Изучите небезопасный Dockerfile:

sed -n '1,120p' Dockerfile.insecure | tee evidence/insecure-dockerfile-review.txt

Рискованная строка — COPY . /app/. Она копирует app.sh, но также копирует build-token.txt. На следующем этапе вы соберете этот образ и докажете наличие утечки изнутри запущенного контейнера.

Сборка уязвимого образа и доказательство наличия секрета

На этом этапе вы соберете небезопасный образ внутри профиля minikube labex-v134. Сборка внутри профиля делает образ видимым для среды выполнения Kubernetes.

Скопируйте небезопасный Dockerfile в основной файл Dockerfile и соберите уязвимый образ:

cp Dockerfile.insecure Dockerfile
minikube -p labex-v134 image build -t labex/leaky-build:1.0 .

Запустите образ внутри среды выполнения Docker в minikube и проверьте наличие файла с токеном:

minikube -p labex-v134 ssh -- 'docker run --rm --entrypoint /bin/sh labex/leaky-build:1.0 -c "ls -l /app && cat /app/build-token.txt"' | tee evidence/leaky-image-contents.txt

Вы должны увидеть build-token=labex-temporary-secret. Это и есть утечка. Образ работает, но содержит файл, который должен был остаться за пределами итогового образа.

Сборка чистого образа с ограниченной областью копирования

На этом этапе вы исправите сборку образа. Важны два изменения: .dockerignore предотвращает попадание токена в контекст сборки, а Dockerfile копирует только app.sh вместо всей директории.

Создайте .dockerignore:

cat > .dockerignore <<'EOF'
build-token.txt
evidence/
*.txt
EOF

Создайте чистый Dockerfile:

cat > Dockerfile <<'EOF'
FROM busybox:1.36

WORKDIR /app
COPY app.sh /app/app.sh
RUN chmod 0555 /app/app.sh

USER 65534:65534
ENTRYPOINT ["/app/app.sh"]
EOF

Соберите чистый образ:

minikube -p labex-v134 image build -t labex/clean-build:1.0 .

Проверьте чистый образ изнутри среды выполнения Docker в minikube:

minikube -p labex-v134 ssh -- 'docker run --rm --entrypoint /bin/sh labex/clean-build:1.0 -c "ls -l /app; if [ -f /app/build-token.txt ]; then echo BUILD_SECRET_PRESENT; else echo NO_BUILD_SECRET; fi; id -u"' | tee evidence/clean-image-contents.txt

Вывод должен показать NO_BUILD_SECRET и UID 65534.

Развертывание чистого образа

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

Создайте и примените манифест пода:

cat > clean-build-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: clean-build-app
  namespace: cks-prep-build-secret
  labels:
    app: clean-build-app
spec:
  restartPolicy: Never
  containers:
    - name: app
      image: labex/clean-build:1.0
      imagePullPolicy: Never
      securityContext:
        runAsNonRoot: true
        runAsUser: 65534
        runAsGroup: 65534
        allowPrivilegeEscalation: false
EOF
kubectl apply -f clean-build-pod.yaml

Дождитесь готовности пода и прочитайте его логи:

kubectl -n cks-prep-build-secret wait --for=condition=Ready pod/clean-build-app --timeout=120s
kubectl -n cks-prep-build-secret logs clean-build-app | tee evidence/clean-pod-logs.txt

Логи должны содержать:

clean build image ready
NO_BUILD_SECRET

Резюме

Вы изучили небезопасный контекст сборки образа, создали уязвимый образ, доказали наличие в нем токена сборки, а затем исправили сборку с помощью .dockerignore и точной инструкции COPY в Dockerfile. Вы собрали и развернули чистый образ, который работает от имени пользователя без прав root и сообщает NO_BUILD_SECRET. Это завершает этап обеспечения безопасности цепочки поставок, связывая гигиену сборки образов с проверкой во время выполнения в Kubernetes.