TT Lab
はじめる
学ぶ 学習パス コース

ビルドは緑だったのに、あのライブラリを入れたのは誰か

このバイト列がどこから来たのか誰も知らない

TT Labで続きを見る

一言でいうと

来歴証明(provenance)は、「何が、何から、誰によって、いつ作られたか」をビルドの時点で書いておくことです。SLSAは、その記録をどれだけ信頼できるかを段階に分けます。

なぜ必要なのか

一覧と署名がそろっていても、答えが出ない質問が1つ残ります。「このバイト列はどこから来たのか」。署名は「誰がこれに署名したか」に答え、SBOMは「何が入っているか」に答えますが、どちらもそのアーティファクトがどのソースからどんな手順で出てきたかは教えてくれません。

この質問が実際に問題になる場面は、劇的ではありません。たいていはこうです。急いでいて、誰かが自分のノートPCでビルドしてアップロードし、そのアーティファクトにも会社の鍵で署名が付いています。署名は通ります。一覧も付いています。ところが、そのビルドがどのコミットから出たのか、どの依存を使ったのか、誰も知りません。数か月後に問題が起きたとき、たどれる糸口が1本もありません。

どう動くのか

SLSAは、これをトラックとレベルに分けます。BuildトラックでL0は何の保証もない状態で、L1は、アーティファクトがどう作られたかを書いた来歴証明が存在することです。L1の証明は、不完全だったり署名されていなかったりするので、ミスは防げますが、偽造は防げません。L2は、専用インフラ上のホスト型ビルドプラットフォームが証明を作って署名することで、そのため、ビルド後の改ざんを防げます。L3は、ビルドプラットフォーム自体を堅牢化して、実行同士が互いに影響を与えられないようにし、証明の署名に使う秘密を、ユーザー定義のビルドステップが触れないようにします(SLSAのセキュリティレベル)。このレベルのドキュメントは、現在v1.0が「Retired」と表示されていて、より新しいバージョンを指しています。引用するときに確認が必要な箇所です。

証明の形は、in-toto Statementを使います。一番上に_typeとsubjectがあり、predicateTypeで述語の種類を指します。SLSAのドキュメントは、ここにURLバーに見えるアドレスではなくhttps://slsa.dev/provenance/v1をそのまま入れるよう固定してあります。述語の内側は、2つに分かれます。

buildDefinition   무엇을 만들라고 했는가
  buildType            이 칸들을 어떻게 읽어야 하는지 가리키는 URI
  externalParameters   빌드에 밖에서 넣은 값 (소스 주소와 커밋, 진입점 등)
  internalParameters   플랫폼이 스스로 채운 값
  resolvedDependencies 실제로 쓴 재료와 그 다이제스트

runDetails        누가 언제 실행했는가
  builder.id           이 빌드를 수행한 플랫폼의 신원
  metadata             invocationId · startedOn · finishedOn

このコードブロックの韓国語の行は、順に、buildDefinition(何を作るよう指示されたか)、buildType(これらの欄をどう読むべきかを指すURI)、externalParameters(ビルドに外から入れた値で、ソースのアドレスとコミット、エントリーポイントなど)、internalParameters(プラットフォームが自分で埋めた値)、resolvedDependencies(実際に使った材料とそのダイジェスト)、runDetails(誰がいつ実行したか)、builder.id(このビルドを実行したプラットフォームのID)、metadata(invocationId・startedOn・finishedOn)を説明している、という意味です。

ドキュメントがBuild L1の必須として書いているのは、buildDefinitionとrunDetails、その中のbuildTypeとexternalParameters、そしてbuilderです(SLSA Provenance v1)。残りは、あると望ましい欄です。特にbuilder.idは、信頼の境界をまるごと背負う欄であることを、ドキュメントが別に説明しています。その識別子が指すプラットフォームを信頼するという宣言だからです。

resolvedDependenciesの価値は、材料ごとにダイジェストを一緒に書く点にあります。名前だけを書くと、「同じ入力でもう一度作れば同じものが出るか」を問えません。ダイジェストがあれば、その質問を、あとで、事故が起きたあとでも投げられます。

現場での姿

最初の落とし穴は、証明を作るのに誰も読まないことです。パイプラインがJSONをもう1つ吐き出し、それをアーティファクトリポジトリにアップロードするところで終わります。デプロイの直前にそのファイルを開いて見る手順がなければ、その証明は、存在するだけでは何も変えません。

2つ目は、externalParametersにビルド環境全体をまるごと注ぐことです。ドキュメントは、この欄を最小に保つよう勧めています。値が増えるほど、検証する側が「何が正常か」を定義しにくくなり、結局、誰も比較しない大きなかたまりになります。

3つ目は、コミットハッシュの代わりにブランチ名を書くことです。ブランチは動くポインターなので、同じ証明が時点ごとに違うソースを指すことになります。ダイジェストで固定する必要があります。

4つ目は、証明を作る主体を取り違えることです。証明は、ビルドを実行した側が作って初めて価値があります。ビルドが終わったあとに人が手で埋めた証明は、その人が知っていることだけが書かれ、その人が騙されれば、証明も一緒に騙されます。SLSAがレベルを上げるほど「誰が証明を作るか」を絞り込んでいく理由が、これです。L2でホスト型プラットフォームが作って署名するようにし、L3ではその署名の秘密を、ユーザーのビルドステップが触れないようにします。

次のクイズで確認すること

Buildトラックの各レベルが何を防ぐのか、証明のどの欄がBuild L1の必須なのか、そしてbuilder.idがなぜ信頼の境界を背負う欄なのかを確認します。続くモジュールでは、この証明を実際に作って署名し、それを読むゲートまで立てます。