Docker Imageの構造を調べた

Sep 8, 2024 22:20 · 1595 words · 4 minute read docker security

Docker、とても便利ですよね。

最近は減ったように思いますが、少し前はDocker ImageからAccess Tokenなどが漏洩する事例が度々報じられていました。

「Docker Imageって確かtarにデータをまとめてて、Layerを解析できるんだったよな」 という知識はあったのですが、実際のデータを解析したことがなかったので解析してみました。

目次

tarファイルとしてDocker Imageを保存する

今回検証に使ったDocker Imageはこちら。

GitHub - kapiecii/haproxy-sample

docker saveコマンドでImageを保存します。 tarファイルとして保存しました。

docker save -o ../docker-image-tar/haproxy-image.tar haproxy-sample:latest

tarファイルの展開とファイル構成

保存したtarファイルを展開します。 展開した後のファイル構成がこちら

tree .
 .
 ├── blobs
 │   └── sha256
 │       ├── 00268a2b175bfaf4b4e241f5085a7ca3f6d989512094080ce9f61ed2d8ded496
 │       ├── 03d8c58f9fec1d4c07decd6d6f170c4dcd62266846cf5cfac73c51bd78712826
 │       ├── 07cf6e018c75611749879c75dca2799e7b9e60f707092d8b91fa85caea067ed1
 │       ├── 28398d1d5df2938fe0f404f613372a50910854c77334253bd98fc4e5945b83c2
 │       ├── 2d540394eb61367544be2f2cfdbbebb10d3caefa885d054f3ee44662d715a26d
 │       ├── 393137c7af7a9a377b1c354d623db48897f91471569e557dcfbf0e6fa757b882
 │       ├── 39f4363fb17c304521a38eda36ccc38ac71f8498cbda7d1ffe3a5afc1465d063
 │       ├── 46a964987937d96a0fe8c91df32604800141e49bf354218a22c6b8e116adcd1b
 │       ├── 5c2648749fab865cefc37ed57961873e6d72425b44d41929a9f081eae3afdf9a
 │       ├── 6a9a0fce25272fee8ea80b23c1c07766cedd597aadd60635b9899ee9ab72d26e
 │       ├── 6db32605a65c1624a8c20ec411274d68048412698080f16e054679a799076993
 │       ├── 86e788000b07aed8300be976d6e58dad5f03b2c2b99abccb893b5efd48fb62d1
 │       ├── 885be708b2b975acef03c5e6db4b2b4618de53584338270ca7a3b38fcfa61a86
 │       ├── 8905e50a86c4dabedc977ef8d50a26a21b98f3a172c924047cd41d64190ca688
 │       ├── b5ebffba54d3e3f7fd80435fcdc34c4a96fdb2ecab0f0a298fe08f74c2f69d29
 │       ├── bcc22f8c9cbb745cbdfb6d7471af92704af2d69f40949e0f27bb80a4893105ee
 │       ├── cc0c9c40e4376be2f8e655525cbd060b7f42d8b024f558f230c7c9d6ad55a519
 │       ├── d326469892d974408d96f1e02d64dce10d20f88613688af11e99e3e22523beeb
 │       ├── d47d86712472c62d826f9659ae5d88a37c2cd4c2d0d2d54d504e32b7e5cddd3f
 │       ├── dabca332051aa14bc7622274415e28f7a31d987c44ddee05c76b05d5359dc4ef
 │       ├── de94270182254d5d9ee6ba65b135e49ae7b0d8c792ca82b1120213035c73afd7
 │       └── ff7e797870217701c5e6eb2d27c416870a275a68891ae275edb5f6a0ca077a7d
 ├── index.json
 ├── manifest.json
 ├── oci-layout
 └── repositories
 
 2 directories, 26 files

index.json

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:28398d1d5df2938fe0f404f613372a50910854c77334253bd98fc4e5945b83c2",
      "size": 1816,
      "annotations": {
        "io.containerd.image.name": "docker.io/library/haproxy-sample:latest",
        "org.opencontainers.image.ref.name": "latest"
      },
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ]
}

manifest.json

[
  {
    "Config": "blobs/sha256/6db32605a65c1624a8c20ec411274d68048412698080f16e054679a799076993",
    "RepoTags": [
      "haproxy-sample:latest"
    ],
    "Layers": [
      "blobs/sha256/b5ebffba54d3e3f7fd80435fcdc34c4a96fdb2ecab0f0a298fe08f74c2f69d29",
      "blobs/sha256/d326469892d974408d96f1e02d64dce10d20f88613688af11e99e3e22523beeb",
      "blobs/sha256/46a964987937d96a0fe8c91df32604800141e49bf354218a22c6b8e116adcd1b",
      "blobs/sha256/8905e50a86c4dabedc977ef8d50a26a21b98f3a172c924047cd41d64190ca688",
      "blobs/sha256/bcc22f8c9cbb745cbdfb6d7471af92704af2d69f40949e0f27bb80a4893105ee",
      "blobs/sha256/39f4363fb17c304521a38eda36ccc38ac71f8498cbda7d1ffe3a5afc1465d063",
      "blobs/sha256/86e788000b07aed8300be976d6e58dad5f03b2c2b99abccb893b5efd48fb62d1",
      "blobs/sha256/dabca332051aa14bc7622274415e28f7a31d987c44ddee05c76b05d5359dc4ef",
      "blobs/sha256/03d8c58f9fec1d4c07decd6d6f170c4dcd62266846cf5cfac73c51bd78712826",
      "blobs/sha256/6a9a0fce25272fee8ea80b23c1c07766cedd597aadd60635b9899ee9ab72d26e"
    ],
    "LayerSources": {
      "sha256:03d8c58f9fec1d4c07decd6d6f170c4dcd62266846cf5cfac73c51bd78712826": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 3072,
        "digest": "sha256:03d8c58f9fec1d4c07decd6d6f170c4dcd62266846cf5cfac73c51bd78712826"
      },
      "sha256:39f4363fb17c304521a38eda36ccc38ac71f8498cbda7d1ffe3a5afc1465d063": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 1536,
        "digest": "sha256:39f4363fb17c304521a38eda36ccc38ac71f8498cbda7d1ffe3a5afc1465d063"
      },
      "sha256:46a964987937d96a0fe8c91df32604800141e49bf354218a22c6b8e116adcd1b": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 31395328,
        "digest": "sha256:46a964987937d96a0fe8c91df32604800141e49bf354218a22c6b8e116adcd1b"
      },
      "sha256:6a9a0fce25272fee8ea80b23c1c07766cedd597aadd60635b9899ee9ab72d26e": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 48111104,
        "digest": "sha256:6a9a0fce25272fee8ea80b23c1c07766cedd597aadd60635b9899ee9ab72d26e"
      },
      "sha256:86e788000b07aed8300be976d6e58dad5f03b2c2b99abccb893b5efd48fb62d1": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 5632,
        "digest": "sha256:86e788000b07aed8300be976d6e58dad5f03b2c2b99abccb893b5efd48fb62d1"
      },
      "sha256:8905e50a86c4dabedc977ef8d50a26a21b98f3a172c924047cd41d64190ca688": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 4608,
        "digest": "sha256:8905e50a86c4dabedc977ef8d50a26a21b98f3a172c924047cd41d64190ca688"
      },
      "sha256:b5ebffba54d3e3f7fd80435fcdc34c4a96fdb2ecab0f0a298fe08f74c2f69d29": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 84000256,
        "digest": "sha256:b5ebffba54d3e3f7fd80435fcdc34c4a96fdb2ecab0f0a298fe08f74c2f69d29"
      },
      "sha256:bcc22f8c9cbb745cbdfb6d7471af92704af2d69f40949e0f27bb80a4893105ee": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 12726272,
        "digest": "sha256:bcc22f8c9cbb745cbdfb6d7471af92704af2d69f40949e0f27bb80a4893105ee"
      },
      "sha256:d326469892d974408d96f1e02d64dce10d20f88613688af11e99e3e22523beeb": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 3405312,
        "digest": "sha256:d326469892d974408d96f1e02d64dce10d20f88613688af11e99e3e22523beeb"
      },
      "sha256:dabca332051aa14bc7622274415e28f7a31d987c44ddee05c76b05d5359dc4ef": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 2560,
        "digest": "sha256:dabca332051aa14bc7622274415e28f7a31d987c44ddee05c76b05d5359dc4ef"
      }
    }
  }
]

Layerの中身

blobs/sha256/の下にはDocker Imageに含まれるLayerの情報や、Imageに関する情報があります。 manifest.jsonのLayersに書いてあるLayerを確認してみます。

Layerのtarファイルを展開するとDocker ImageにCopyした各種ファイルが含まれています。 このPythonファイルはHAProxyの動作確認をしたときのコードです。

├── 86e788000b07aed8300be976d6e58dad5f03b2c2b99abccb893b5efd48fb62d1
│   └── app
│       ├── fastapi-app.py
│       ├── haproxy.cfg
│       └── install.sh

haproxy-sample/src at main · kapiecii/haproxy-sample · GitHub

また、.envで設定した認証情報も取り出せます。 .envを.gitignoreに入れていたとしても、Docker Imageから認証関連情報が漏洩する可能性があります。

cat ./dabca332051aa14bc7622274415e28f7a31d987c44ddee05c76b05d5359dc4ef/app/.env
HAPROXY_STATS_USER={USER NAME}
HAPROXY_STATS_PASSWORD={PASSWORD}

認証用のTokenなどは、.envではなくdocker container実行時に引数として渡します。 App Runnerなどを使う場合は、サービスが提供しているSecret管理の機能を使います。

blobs/sha256のハッシュ値

blobs/sha256/の下にあるディレクトリ名やファイル名は、tarなどのデータをsha256でハッシュ化した値です。

sha256sum blobs/sha256/6a9a0fce25272fee8ea80b23c1c07766cedd597aadd60635b9899ee9ab72d26e
6a9a0fce25272fee8ea80b23c1c07766cedd597aadd60635b9899ee9ab72d26e  blobs/sha256/6a9a0fce25272fee8ea80b23c1c07766cedd597aadd60635b9899ee9ab72d26e

ハッシュ値をキーにしてLayerを管理しているようです。 他にも、

  • ハッシュ値を使ってLayerの改ざんを検知している?
  • ハッシュ値を使って重複したLayerは使い回している?

という情報も見かけたのですが、元情報までは見つけられなかったので真偽の程はわかりません。 Dockerの仕様やソースコードを探したら正確な情報がわかると思いますが、残念ながらそこまで調べる時間がありませんでした。 今後の調査課題ですね。

最後に

今更ながらDocker Imageの中身を確認しました。 手を動かして確認したおかげで、ふんわりと知っていた知識が納得感のある知識になりました。

TrivyなどのScannerが何をしているのかもわかったので、自分で便利ツールを作ることもできそうですね。

tweet Share