イメージからビルドシークレットを削除する

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 もコピーしてしまいます。次のステップでは、このイメージをビルドし、イメージの実行環境内から漏洩を証明します。

漏洩するイメージのビルドとシークレットの存在確認

このステップでは、labex-v134 minikube プロファイル内で不安全なイメージをビルドします。プロファイル内でビルドすることで、Kubernetes ノードのランタイムからイメージが見えるようになります。

不安全な Dockerfile をアクティブな Dockerfile 名にコピーし、漏洩するイメージをビルドします:

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

minikube の Docker ランタイム内でイメージを実行し、トークンファイルを確認します:

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 が表示されるはずです。これが漏洩です。イメージは実行されますが、本来は最終的なランタイムイメージの外にあるべきファイルが含まれてしまっています。

コピー境界を絞り込んだクリーンなイメージのビルド

このステップでは、イメージのビルドを修正します。重要な変更点は 2 つです。.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 .

minikube の Docker ランタイム内からクリーンなイメージを調査します:

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 環境に近い状態で最終的な証明を行います。

Pod マニフェストを作成して適用します:

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

Pod が準備完了になるのを待ち、ログを読み取ります:

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 と Dockerfile の COPY 命令を絞り込むことでビルドを修正しました。非 root ユーザーとして実行され、NO_BUILD_SECRET を報告するクリーンなイメージをビルドおよびデプロイしました。これにより、イメージビルドの衛生管理と Kubernetes ランタイムの検証を結びつけ、サプライチェーンのステージを完了しました。