モジュールミラーとチェックサムデータベースを公開しました
Module Mirror and Checksum Database Launched by Katie Hockman
モジュールミラー、インデックス、チェックサムデータベースが本番運用の準備が整ったことを、私たちは喜んでお知らせします。go コマンドは、Go 1.13のモジュールユーザーに対してデフォルトでモジュールミラーとチェックサムデータベースを使用します。これらのサービスに関するプライバシー情報についてはproxy.golang.org/privacyを、これらのサーバーの利用を無効化する方法や別のサーバーを利用する方法などの設定の詳細についてはgoコマンドのドキュメントを参照してください。非公開のモジュールに依存している場合は、環境設定に関するドキュメントを参照してください。
この記事では、これらのサービスとそれらを利用する利点について説明し、Gophercon 2019でのGo Module Proxy: Life of a Queryというトークの内容の一部をまとめます。トーク全体に興味がある方は録画をご覧ください。
モジュールミラー
モジュールとは、まとめてバージョン管理されるGoパッケージの集合であり、各バージョンの内容はイミュータブルです。このイミュータブルという性質によって、キャッシュや認証のための新しい可能性が生まれます。go get がモジュールモードで実行されると、要求されたパッケージを含むモジュールと、そのモジュールによって導入される新しい依存関係の両方を取得し、必要に応じてgo.modとgo.sumファイルを更新しなければなりません。バージョン管理システムからモジュールを取得するのは、レイテンシとシステム上のストレージの観点でコストがかかることがあります。go コマンドは、たとえビルドされることのない推移的な依存関係であっても、そのバージョンを解決するためだけに、依存先リポジトリの全コミット履歴を取得せざるを得ないことがあるのです。
この解決策は、go コマンドのニーズによりよく適合したAPIを話すモジュールプロキシを使うことです(go help goproxy を参照してください)。go get がプロキシを使ってモジュールモードで実行されると、必要な特定のモジュールのメタデータやソースコードだけを要求し、それ以外は気にしないことで、より高速に動作します。以下は、go コマンドが go get でプロキシを利用する際に、バージョンの一覧を要求し、続いて最新のタグ付きバージョンのinfo、mod、zipファイルを要求する例です。

モジュールミラーは、メタデータとソースコードを自身のストレージシステムにキャッシュする特殊な種類のモジュールプロキシであり、元の場所からはもう入手できなくなったソースコードを提供し続けることができます。これによってダウンロードを高速化でき、依存関係が消えてしまうことからあなたを守ります。詳しくはGo Modules in 2019を参照してください。
Goチームはproxy.golang.orgで提供されるモジュールミラーを維持しており、Go 1.13以降では go コマンドがモジュールユーザーに対してデフォルトでこれを使用します。それより前のバージョンの go コマンドを使っている場合は、ローカル環境で GOPROXY=https://proxy.golang.org を設定することでこのサービスを利用できます。
チェックサムデータベース
モジュールは go.sum ファイルを導入しました。これは、各依存関係が最初にダウンロードされたときの、ソースコードと go.mod ファイルのSHA-256ハッシュの一覧です。go コマンドはこれらのハッシュを使って、同じバージョンに対して異なるコードを渡してくるオリジンサーバーやプロキシの不正な振る舞いを検出できます。
この go.sum ファイルの限界は、完全に あなた自身 の最初の利用時の信頼に基づいて動作している点です。これまで見たことのないバージョンの依存関係をモジュールに追加すると(既存の依存関係をアップグレードする場合などに起こり得ます)、go コマンドはそのコードを取得し、その場で go.sum ファイルに行を追加します。問題は、その go.sum の行が他の誰かのものと照合されていないということです。たとえばプロキシがあなたを狙って意図的に悪意のあるコードを提供していたなどの理由で、go コマンドが他の誰かのために生成したばかりの go.sum の行とは異なっている可能性があるのです。
Goの解決策は、チェックサムデータベースと呼ばれる go.sum の行のグローバルな情報源を用意することです。これにより、go コマンドが誰の go.sum ファイルにも常に同じ行を追加することが保証されます。go コマンドは新しいソースコードを受け取るたびに、そのコードのハッシュをこのグローバルデータベースと照合してハッシュが一致することを確認でき、あるバージョンについて誰もが同じコードを使っていることを保証できます。
チェックサムデータベースはsum.golang.orgによって提供されており、Trillianに支えられたハッシュのTransparent Log(あるいは「マークル木」)の上に構築されています。マークル木の主な利点は改ざんが困難であり、不正な振る舞いが検出されずに済んでしまうことを許さない性質を持っている点で、これによって単純なデータベースよりも信頼性が高くなっています。go コマンドはモジュールの go.sum ファイルに新しい行を追加する前に、この木を使って「包含性 (inclusion)」の証明と「一貫性 (consistency)」の証明を確認します。前者はあるレコードがログの中に存在することを示す証明であり、後者は木が改ざんされていないことを示す証明です。以下はそのような木の例です。

チェックサムデータベースは、go コマンドが go.sum の行を要求し検証するために使う一連のエンドポイントをサポートしています。/lookup エンドポイントは「署名付きツリーヘッド (STH)」と要求された go.sum の行を提供します。/tile エンドポイントは、go コマンドが証明に使用できる、木の断片である タイル を提供します。以下は、go コマンドがあるモジュールバージョンの /lookup を行い、続いて証明に必要なタイルを要求することで、チェックサムデータベースとやり取りする例です。

このチェックサムデータベースによって、go コマンドは本来であれば信頼できないプロキシを安全に利用できるようになります。その上に監査可能なセキュリティ層が存在するため、プロキシやオリジンサーバーが、意図的であれ、恣意的であれ、あるいは偶発的であれ、誤ったコードを提供し始めれば、必ず検出されます。モジュールの作者であっても、ある日から次の日にかけてタグを付け替えたり、特定のバージョンに紐づくビットを変更したりすれば、その変更は検出されてしまいます。
Go 1.12以前を使っている場合は、gosumcheckを使って go.sum ファイルを手動でチェックサムデータベースと照合できます。
$ go get golang.org/x/mod/gosumcheck
$ gosumcheck /path/to/go.sum
go コマンドによる検証に加えて、サードパーティの監査者はログを走査して不正なエントリがないかを調べることで、チェックサムデータベースの説明責任を担うこともできます。監査者たちは協力し合い、木が成長していく過程でその状態について情報交換 (gossip) することで、それが改ざんされていないことを確認できます。私たちはGoコミュニティがこうした監査者を運用してくれることを望んでいます。
モジュールインデックス
モジュールインデックスはindex.golang.orgによって提供されており、proxy.golang.orgを通じて利用可能になった新しいモジュールバージョンの公開フィードです。これは特に、proxy.golang.orgで利用可能なものの独自のキャッシュを保持したいツール開発者や、人々が使っている最新のモジュールの情報を追い続けたいツール開発者にとって役立ちます。
フィードバックやバグ報告
これらのサービスによってモジュールを使う際の体験がより良いものになることを願っています。問題に遭遇したり、フィードバックがあったりした場合には、ぜひissueを立ててください!
By Katie Hockman