メインコンテンツまでスキップ

Nix のビルド入力の導出

各イメージのビルド入力は、Nix のビルドグラフから機械的に導出できます。 この導出は、SBOM の生成、上流のマルウェア検知、上流のゼロデイ修正検知という、性質の異なる複数の機能に共通する基盤です。

導出とは

Nix でのビルドは、すべて導出(derivation)という単位で宣言します。 導出は、依存する導出、ソース、ビルドスクリプトといった入力を持ち、それぞれの入力はハッシュで固定されています。 同じ入力からは必ず同じ出力ができるという性質は、この宣言のしかたから直接来ています。 イメージそのものもひとつの導出で、その入力をたどると、依存するライブラリの導出、そのソースの導出というふうに、ビルドグラフ全体が木構造で広がっています。 あるイメージが実際に何を取り込んでビルドされたかは、人が書いたドキュメントではなく、このグラフそのものが正本です。

導出の中身は、nix derivation show で誰でも直接確認できます。 たとえば nixpkgs の hello パッケージを確認すると、ビルドスクリプトを実行する builder、その引数、そして依存する導出(inputDrvs)が、次のように JSON で得られます(実際の出力から一部を抜粋)。

$ nix --extra-experimental-features 'nix-command flakes' \
derivation show 'nixpkgs#legacyPackages.x86_64-linux.hello'
{
"name": "hello-2.12.3",
"system": "x86_64-linux",
"builder": "/nix/store/zh1ijdhb6gng1509b1zrilb6xlzx60j6-bash-5.3p9/bin/bash",
"args": [
"-e",
"/nix/store/l622p70vy8k5sh7y5wizi5f2mic6ynpg-source-stdenv.sh",
"/nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh"
],
"inputDrvs": [
"9w4h6mwgy4vivksdb54jhz6i2dihqv1l-hello-2.12.3.tar.gz.drv",
"g7p7rs03xah2w00a142hzas9h4fkwpck-bash-5.3p9.drv",
"i1fmvnw4pqbjhcygp8rggs6n88jg1bny-version-check-hook.drv",
"l0x59a9gpwqh6rxxx2sxbl5nd72r4bc8-stdenv-linux.drv"
]
}

inputDrvs に並ぶのは、この導出が依存する他の導出です。 それぞれをさらに nix derivation show でたどれば、ソース取得の導出に行き着くまで、ビルドグラフを手作業でも下れます。 このソース取得の導出こそが、次に述べる固定出力導出(FOD)です。

固定出力導出(FOD)とネットワークアクセス

このグラフのなかで、外部ネットワークへのアクセスが許される導出は、固定出力導出(fixed-output derivation、FOD)だけです。 通常の導出はサンドボックスの中でビルドし、宣言した入力以外(ネットワークや宣言外のファイル)へのアクセスを遮断します。 FOD だけは例外的にネットワーク取得を許されますが、そのかわり、取得する結果のコンテンツハッシュを事前に宣言しなければなりません。 取得した結果が宣言したハッシュと一致しなければ、ビルドはその場で失敗します。

観点通常の導出固定出力導出(FOD)
ネットワークアクセス許されない許される
出力のハッシュビルドした結果から決まる事前に宣言し、一致しなければビルド失敗
使われる場面依存関係のコンパイルや組み立て上流ソースの取得(tarball、Git リポジトリなど)

nixpkgs のソース取得関数は、tarball を取ってくるものも Git リポジトリを取ってくるものも、最終的にすべてこの FOD に行き着きます。 FOD は、宣言したハッシュを信頼する形でネットワーク取得を安全に切り出した、ビルドグラフ上で唯一の外部への窓口です。

上流ソースの機械的な導出

ビルドグラフをたどって FOD だけを取り出せば、イメージに寄与した外部ソースの全量を機械的に導出できます。 各 FOD には、取得元の URL やリビジョン、宣言したハッシュが、そのまま属性として残っています。 人がイメージの定義やパッチを読んで「どこから来たか」を推測する必要はなく、ビルドグラフを評価するだけで、この一覧が得られます。 一覧には、イメージ本体のパッケージだけでなく、ビルドに使うツール自体のソースも含まれます。

得られる一覧は、たとえば次のような形です(実際の一覧から 1 ソース分を抜粋)。

{
"https://github.com/chainguard-dev/apko.git": {
"fetcher": "git:github.com",
"rev": "refs/tags/v1.2.19",
"hash": "sha256-Iia0U/oibPuUYC3adeXvL5m4nhEPHqBvHw7J7Uxn88s=",
"images": ["apko"]
}
}

どの上流ソースが、どのリビジョンで、どのイメージに影響するか(images)までが機械的に決まります。 上流で新しいコミットやリリースが現れたとき、この一覧との差分を取れば「まだ取り込んでいない変更の全量」が得られ、影響先のイメージも即座に分かります。

突き合わせ先を上流の最新リリースやコミットに取ると、pin がそこからどれだけ先へ進んでいるかが分かります。

3つの使いみち

この一覧は、性質の異なる3つの場面で使います。

1つめは、SBOM の生成です。 SBOM が列挙するのは、実際に組み上がったイメージのランタイム依存 closure です。 FOD の一覧と同じくビルドグラフの評価から機械的に得るもので、動いているコンテナの中身を正確に表します。

2つめは、上流のマルウェア検知です。 FOD の一覧が指す上流ソースを継続的に監視し、悪意ある変更の兆候を探します。

3つめは、上流のゼロデイ修正検知です。 同じ監視の対象について、まだ脆弱性 ID として公表されていない修正がないかを調べます。