2024/08/31 Trivyのソースコードを読む
Trivyのコードリーディング
imageコマンドを入力する。
コマンドのハンドラが呼び出される(RunE)
artifact.Run()にコマンドのContextとartifact.TargetContainerImageを指定して実行する
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/commands/app.go#L318
ここが処理の入口。
artifact.TargetContainerImageが指定された場合はRunner.ScanImageをコールする
また、今回は読まないがフィルタやレポートのコールもここでやっている
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/commands/artifact/run.go#L370
今回は単純にimage名のみを指定したときの処理を読む。
r.scanArtifactにimageStandaloneScannerが渡されて実行される。
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/commands/artifact/run.go#L167
今回の仮定では、initalizeScannerは渡されているといえるので、r.scanが呼び出される
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/commands/artifact/run.go#L258
今回の仮定でr.scanはimageStandaloneScanneを呼び出す
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/commands/artifact/run.go#L607
imageStandaloneScannerを呼び出すと、initializeImageScannerを呼び出してそのまま返す
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/commands/artifact/scanner.go#L11
initializeImageScannerはWireで注入されたStandaloneDockerSetを呼び出して返す?
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/commands/artifact/inject.go#L22
initializeImageScannerの実体はおそらくこれです
戻り値としてscanner.NewScanner()を返している
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/commands/artifact/wire_gen.go#L35
scanner.NewScanner()の戻り値のscanArtifactメソッドは何?
scanner.NewScanner()
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/scanner/scan.go#L147
引数がDriver, ScanArtifact
DriverはLocalScanner
local.NewScanner()
ScanArtifactはArtifactArtifact
image2.NewArtifact()
scanArtifact
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/scanner/scan.go#L155
artifactにはInspect()が生えてる
driverにはScan()が生えてて、呼び出し時にInspectの戻り値を渡してる
Inspect()の実装
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/fanal/artifact/image/image.go#L76
imageIDとかはどうやって取ってるんだろう?
NewArtifact呼び出し時点でImageを貰ってるから、呼び出し側でimageを取得しているみたい
その値はtypesImageっぽい
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/commands/artifact/wire_gen.go#L46
image.newContainerImage()の戻り値
image.newContainerImage()
https://github.com/aquasecurity/trivy/blob/bf64003ac8b209f34b88f228918a96d4f9dac5e0/pkg/fanal/image/image.go#L26
imageOpt.imageSourcesに型が入っており、それがtypes.DockerImageSourceであればtryDockerDaemon()にimageNameを渡して呼び出す
types.DockerImageSourceの実体は文字列「docker」
https://github.com/aquasecurity/trivy/blob/0cac3ac7075017628a21a7990941df04cbc16dbe/pkg/fanal/types/image.go#L9
ImageSourceとは、コンテナレジストリやコンテナランタイムなどを識別するための文字列
https://github.com/aquasecurity/trivy/blob/0cac3ac7075017628a21a7990941df04cbc16dbe/pkg/fanal/types/image.go#L70
Optionsは結局Flag(コマンド引数)周りの処理をソースとするデータ。デフォルトだとAllImageSourcesと同一になりそう
https://github.com/aquasecurity/trivy/blob/0cac3ac7075017628a21a7990941df04cbc16dbe/pkg/flag/image_flags.go#L57
tryDockerDaemonは、imageName(string)ではなくname.Referenceのみを用いてdaemon.DockerImageを呼び出す。
https://github.com/aquasecurity/trivy/blob/0cac3ac7075017628a21a7990941df04cbc16dbe/pkg/fanal/image/daemon.go#L12
tryDockerDaemonの下記の部分で、Docker daemonに対するリクエストを行っている。イメージ情報の取得処理はここが中核と考えられる
https://github.com/aquasecurity/trivy/blob/0cac3ac7075017628a21a7990941df04cbc16dbe/pkg/fanal/image/daemon/docker.go#L39
イメージ情報の取得はclient.NewClientWithOps()の戻り値オブジェクトに実装されている。
これはDocker APIのGo言語によるラッパーである。
ここまでのまとめ
opts.ImageSourcesはコンテナランタイムなどの識別情報だが、ユーザが直接指定しない限り定義済みのすべての値が入る。
image.NewContainerImage()で、各ImageSourceに対してtrySrc系関数の呼び出しを試行し、nil以外のものが返ってきたら戻り値として返す。
tryDockerDaemon()はdaemon.DockerImage()を呼び出す。
daemon.DockerImage()は、Docker APIを呼び出し、inspectやhistoryを持つオブジェクト daemon.Imageを返す。
tryDockerDaemon()は、取得したdaemon.ImageをImageプロパティに、引数で渡されたimageNameをnameにセットしたtypes.Imageを返す(取得できなければnil)。
まとめ
image.NewContainerImageは、内部でDocker APIなどのDaemonに関するAPIを呼び出し、types.Imageまたはnilを返す。
daemon.Image(types.Imageにおけるimageプロパティ)
history
https://pkg.go.dev/github.com/docker/docker@v27.2.0+incompatible/api/types/image#HistoryResponseItem
inspect
https://pkg.go.dev/github.com/docker/docker@v27.2.0+incompatible/api/types#ImageInspect
感想
IDEは使おう
静的型付け言語であれば簡単に型定義に飛べる。