例えば、Docker コンテナ内で認証情報を必要とするコマンド(例:AWS CLI など)を実行したい場合に、コンテナ内に認証情報ファイルをコピーして、コマンドを実行したらすぐに削除したとします。
FROM alpine:3.21.3
# 認証情報ファイルをコピー
COPY credentials /tmp/credentials
# コマンド実行後に認証情報ファイルを削除
RUN 認証情報が必要なコマンド && rm -f /tmp/credentials
コンテナを起動してコンテナ内に入ると/tmp/credentials
は存在していません。
RUN
でファイルを削除しているためです。
一見するとセキュリティ面では問題なさそうに思えますが、RUN
のイメージレイヤで認証情報ファイルは削除されたものの、COPY
のレイヤにはファイルが残ったままになっているため危険であると言われています。
この事は知識としては知っていたのですが、ふと、このファイルを復元(取り出し)できるのか?が気になり、検証を行ってみました。
先に結論から書きますと、ファイルの復元ができました。
検証環境
- Ubuntu 22.04.5 LTS (ホスト機)
- Docker version 28.0.1
認証情報ファイルが存在しないことを確認する
まずは、作業用のディレクトリを作成します。
mkdir ~/work
cd ~/work
Dockerfile
を作成します。中身は先ほど登場したものと同じです。
認証情報が必要なコマンド
の箇所は(AWS CLI のコマンドでも良かったのですが)簡単に使えるtouch /tmp/test.txt
で代替します。
FROM alpine:3.21.3
# 認証情報ファイルをコピー
COPY credentials /tmp/credentials
# コマンド実行後に認証情報ファイルを削除
RUN touch /tmp/test.txt && rm -f /tmp/credentials
credentials
ファイルを作成します。検証用なので中身は適当です。
$ echo "abcd1234" > credentials
ディレクトリ構造を確認します。
work
├── credentials
└── Dockerfile
Docker コンテナイメージをビルドします。
$ docker build -t test-credentials:v1 .
コンテナを起動します。
$ docker run -it --rm test-credentials:v1 /bin/sh
コンテナ内でls
コマンドを実行します。
/tmp/credentials
が存在しないことを確認できました。
$ docker run -it --rm test-credentials:v1 /bin/sh
/ # ls /tmp
test.txt
/ #
認証情報ファイルを復元する
では、本題の復元作業に入ります。
まずは、コンテナイメージの履歴を確認します。
上から2つ目が認証情報ファイルをコピーした履歴のようです。
$ docker history test-credentials:v1
IMAGE CREATED CREATED BY SIZE COMMENT
baaa7f5efecb 3 hours ago RUN /bin/sh -c touch /tmp/test.txt && rm -f … 0B buildkit.dockerfile.v0
<missing> 3 hours ago COPY credentials /tmp/credentials # buildkit 10B buildkit.dockerfile.v0
<missing> 2 months ago CMD ["/bin/sh"] 0B buildkit.dockerfile.v0
<missing> 2 months ago ADD alpine-minirootfs-3.21.3-x86_64.tar.gz /… 7.83MB buildkit.dockerfile.v0
次に、コンテナイメージを tar ファイルに保存します。
$ docker image save -o image.tar test-credentials:v1
tar ファイルを展開します。
mkdir my_image
tar -xf image.tar -C my_image
$ tree ./my_image
./my_image
├── blobs
│ └── sha256
│ ├── 08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350
│ ├── 43f60cf296e3de5b33a7e40bd413fb1b6bef27cdfd6511a9c62ec1d67dfa9282
│ ├── 45323140c02695850874d0d9d6b5604630e1db83771f2fe6217a510b36831e87
│ ├── 546e58a06b0be2997519913950b91459021da27c98a02aed826ab6fd0fb45e33
│ ├── a7b3e0a86c7b50efa82ae1162d4ef1c10164eaf17c3bc2886790a10ab1fb0b2f
│ ├── baaa7f5efecb76eb5857eed6791c35191cdcdea97081bf9a43c4fcc61ad0a7dd
│ ├── cf9c58598bc7725d655275a489b8c78fa54e9b386ce05806dd3d92dee05764bf
│ └── d60bba0bcb4bd87500469f4d9a40c42401516589cb0c3fdb311ea5feb1062b4e
├── index.json
├── manifest.json
├── oci-layout
└── repositories
manifest.json
ファイルを確認します。
Layers
にはレイヤの実行順序が定義されているようなので、上から2つ目の546e58a0...
がコピーのレイヤだと当たりを付けます。
[
{
"Config": "blobs/sha256/baaa7f5efecb76eb5857eed6791c35191cdcdea97081bf9a43c4fcc61ad0a7dd",
"RepoTags": [
"test-credentials:v1"
],
"Layers": [
"blobs/sha256/08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350",
"blobs/sha256/546e58a06b0be2997519913950b91459021da27c98a02aed826ab6fd0fb45e33",
"blobs/sha256/a7b3e0a86c7b50efa82ae1162d4ef1c10164eaf17c3bc2886790a10ab1fb0b2f"
],
"LayerSources": {
"sha256:08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350": {
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"size": 8120832,
"digest": "sha256:08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350"
},
"sha256:546e58a06b0be2997519913950b91459021da27c98a02aed826ab6fd0fb45e33": {
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"size": 2560,
"digest": "sha256:546e58a06b0be2997519913950b91459021da27c98a02aed826ab6fd0fb45e33"
},
"sha256:a7b3e0a86c7b50efa82ae1162d4ef1c10164eaf17c3bc2886790a10ab1fb0b2f": {
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"size": 3072,
"digest": "sha256:a7b3e0a86c7b50efa82ae1162d4ef1c10164eaf17c3bc2886790a10ab1fb0b2f"
}
}
}
]
blobs/sha256/546e58a0...
の正体は tar ファイルなので、これを展開します。
※LayerSources
のmediaType
にtar
と書かれてある。
cd my_image
mkdir layer
tar -xf blobs/sha256/546e58a06b0be2997519913950b91459021da27c98a02aed826ab6fd0fb45e33 -C layer
展開先ディレクトリの中身を確認します。
ファイルを復元できました。
$ tree ./layer
./layer
└── tmp
└── credentials # 認証情報ファイル
2 directories, 1 file
$ cat ./layer/tmp/credentials
abcd1234
さいごに
筆者は使ったことがありませんが、dive という Docker イメージレイヤを視覚的に調査できるツールがあるようで、これを使えばもっと簡単にできたりするのかも知れません。