コンテンツにスキップ

イメージ識別モデルの開発

  • 状態: 実装済み(イメージ実体の識別まで / Docker・単一ホスト。containerとサービスの対応付けや複数ホストの識別は今後)
  • 開始日: 2026-08-22
  • 最終更新: 2026-08-23

目的

現在のKestreLynxは、Dockerから取得したイメージ名を起点にスキャンしている。

しかし、イメージ名によるスキャンはexample/api:latestのような可変タグの場合、別の内容に紐づくことがある。
同じイメージを複数のcontainerやWorkloadが使うことがあり、名前を識別子として扱い続けると、以下の問題がある。

  • どのイメージをスキャンしたのか
  • そのイメージをどのWorkloadが使用しているのか
  • 異なる名前のイメージが同じ実体を指しているか
  • 同じ実体をスキャンしたのか、置き換わった実体をスキャンしたのか

この前提をread-onlyの共通識別モデルへ置き換える。
DockerやKubernetesなど実行環境ごとに異なる「コンテナとイメージの取得処理」を実装して、脆弱性スキャン、優先度判定、差分管理、通知は共通処理として再利用できる構成にする。

実装方針

「どの環境で、どのサービスが、どのイメージを実行しており、そのイメージからどの脆弱性が見つかったか」を一続きに追跡できるようにする。
例えば、Dockerホスト、Composeサービス、稼働中のコンテナ、使用中のイメージ、スキャン結果、検出された脆弱性を関連付けて記録する。 イメージはnginx:latestのような表示名だけでなく、実際の内容を特定できるsha256:...形式のdigestでも識別する。

Dockerホスト: home-server
  └─ Composeサービス: myapp / web
       ├─ 稼働中コンテナ: myapp-web-1
       └─ 使用イメージ
            ├─ 表示名: ghcr.io/example/myapp:latest
            └─ 実体: sha256:abc123...
                 └─ スキャン結果
                      └─ パッケージ: openssl
                           └─ 脆弱性: CVE-2026-12345

今後Kubernetesへ対応する場合も、PodやDeploymentから取得した情報を同じ関係へ整理することで、既存のスキャン・解析・通知処理を利用できるようにする。
Docker連携を最初のRuntime Adapterとして実装し、Adapterは稼働中のオブジェクトを発見し、共通モデルへ変換する。 スキャン、脆弱性解析、状態比較、通知は共通の処理として維持する。

制約

  • 発見処理はread-onlyを維持する。
  • 既存のDocker利用者は、現在と同じ基本的なセットアップと通知動作を継続できる必要がある。
  • 内部のスキャン識別子を不変なdigestへ移しても、人が読めるimage referenceは表示に残す。
  • Runtime固有の識別子を共通解析モデルへ漏らさない。
  • 識別子を取得できない、または意味が曖昧な場合は、推測せずその状態を表現する。

ロジック

  • イメージ実体の識別には、Dockerが報告するImageID(image configのdigest)を使用する。
    • レジストリ側のdigest(RepoDigests)は、ローカルでビルドしたイメージには存在せず、複数の値を持つこともあるため識別子には使わず、表示用の属性として保持する。
    • digestを取得できない場合は推測せず「未確認」として扱い、その状態を通知にも明示する。
  • スキャンはイメージ名ではなくdigestを指定して実行する。
    • 名前で指定すると、スキャン直前にタグの指す中身が入れ替わった場合に、稼働中とは別のイメージをスキャンする恐れがある。
    • digest指定により、実際に動いているイメージそのものを確実にスキャンする。
  • スキャン履歴と通知の単位は、従来どおりイメージ名のまま維持する。
    • 履歴のキーをdigestへ変えると、イメージを更新するたびに全脆弱性が「解決済み+新規」として再通知されてしまうので、イメージ名をキーに保つことで既存のstateファイルは変換なしでそのまま使え、移行処理も不要になる。
    • digestは追加情報として記録し、イメージが置き換わった場合は1行の情報として通知に加える。
  • 本フェーズの対象は単一のDockerホストとする。
    • 複数ホストやKubernetesへ拡張する際は、識別子の意味(image configのdigest)を共通に保ったままAdapterを追加する。
    • 実行環境からこの意味でのdigestを取得できない場合は、未確認として扱う。

決定事項

検討中だった2つの課題は、本フェーズの適用範囲を単一Dockerホストに固定することで、次のとおり決定した。

  • Dockerホストの識別は本フェーズでは行わない。 複数ホストの履歴を1つのstateで扱うには「どのホストの記録か」を示す識別子が必要だが、hostnameはKestreLynx自身がコンテナ内で動くため素直に取得できず、daemon IDやmachine-idも再インストールで変わるため、再起動やcontainer再作成をまたいで安定して取得できる方法が思い浮かばなかった。本フェーズは単一ホスト前提とすることでこの識別自体を不要にした(stateのimage reference + packageキーは単一ホスト前提でのみ衝突しない)。複数ホストや複数Runtimeを扱うのは将来フェーズで扱い、その際はRuntime由来の揺らぐ識別子ではなく、設定で与える安定したscopeキー(Environment / Workload)を履歴の主体へ追加する。
  • 「このcontainerはどのサービスのものか」の対応付けは、本フェーズでは行わない。 Composeで起動したcontainerはラベル(com.docker.compose.service等)を見れば所属するサービスが分かるが、docker run等で直接起動したcontainerには、どのサービスのものかを示す情報がそもそも存在しない。本フェーズではcontainerとサービスの対応付け自体を扱わず、Composeラベルやcontainer IDなどRuntime固有の情報はDocker adapterの内部に留め、共通モデルへはRef / ContentID / RegistryDigestsのみを渡す。この対応付けは将来のWorkloadモデルで扱う。

更新履歴

2026-08-23

  • イメージ識別モデルを実装した。Dockerが報告するImageIDを境界検証(sha256:+64桁16進)した値をContentIDとし、解決済みイメージはContentID指定(--image-src docker)でスキャン、未解決イメージはreference指定に切り替えて「稼働実体との一致未確認」を通知に明示する。
  • stateはキー・versionとも従来のまま、ContentIDImages(reference単位のdigest集合)を追加フィールドとして記録し、イメージ置換を1行の通知として配線した。既存stateファイルは無変換で読めることを本番stateのコピーで確認した。
  • 同一referenceの一部実体のみスキャン失敗した場合は、前回とのfindings保守マージと解決判定の保留で誤通知を防ぐ。
  • 検討中だった課題2件(ホストの識別方法・containerとサービスの対応付け)は、単一ホスト前提の明確化により決定事項へ移動した。

2026-08-22

  • 可変タグでは稼働中と異なるイメージをスキャンする可能性があるため、表示名と実体の識別子を分ける方針とした。
  • RepoDigestsはローカルイメージに存在せず複数の値を持つ場合もあるため、実体の識別にはImageIDを採用し、スキャンもdigestで実行する方針とした。
  • 履歴のキーをdigestへ変えると不要な再通知が発生するため、履歴と通知はイメージ名を維持し、digestは実体の変更を示す追加情報とした。
  • 最初の対象は単一のDockerホストとし、複数ホストやKubernetesにはRuntime Adapterの追加で対応する方針とした。