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

閉域網のミラーとプライベート CA

モジュールキャッシュはそのままプロキシになる

TT Labで続きを見る

一言でいうと

Goモジュールのエアギャップ環境への対応は、2つの方向があります。モジュールプロキシをファイルとして立てること(GOPROXY=file://)と、依存関係をリポジトリに入れてしまうこと(vendor)です。どちらの場合も、go.sumが持ち込みマニフェストの役割を果たし、環境変数1行(GOFLAGS)が、どちらの道を行くかを変えます。

なぜ必要なのか

エアギャップ環境のビルドサーバーでgo buildを実行すると、Get "https://proxy.golang.org/...": dial tcp ...で終わります。Goは、デフォルトでGOPROXY=https://proxy.golang.org,directからモジュールを取得し、初めて見るモジュールのハッシュは、sum.golang.orgに問い合わせます。どちらも、エアギャップ環境では届きません。接続されたPCで$GOPATH/pkg/modを丸ごとコピーして持っていく人もいますが、そのディレクトリは、読み取り専用の権限がかかった展開済みのソースなので、コピーや削除から問題を起こし、何を持っていったかの一覧も残りません。

どう動くのか

GOPROXYの文法: Goモジュールのドキュメントによると、GOPROXYは、カンマやパイプでつないだ一覧です。カンマの後ろへは、404・410のときだけ進み、パイプの後ろへは、タイムアウトを含むすべてのエラーで進みます。offは、どこからも取得せず、directは、バージョン管理リポジトリから直接取得します。アドレスのスキームは、https・http・fileのいずれかです。

モジュールキャッシュがそのままプロキシになります: ドキュメントは、GOPROXY=file://$(go env GOMODCACHE)/cache/downloadを例に挙げて、モジュールキャッシュを、そのままファイルプロキシとして使えると書いています。cache/downloadの下には、モジュールごとに<버전>.mod・.zip・.ziphashが(プレースホルダーはバージョンです)、バージョンを問い合わせたモジュールには、@v/list・.infoも、プロキシのプロトコルと同じパスで積まれています。ビルドは、go.modとgo.sumにバージョンが書かれていれば、.modと.zipだけを取得します。このラボで、擬似バージョンとしてついてきたgolang.org/x/textには、.infoがなかったのに、ビルドができました。そのため、接続された側で、新しいモジュールキャッシュ(GOMODCACHE)で一度取得してから、そのディレクトリを移せば、持ち込み一覧とプロキシが、一度にできます。新しいキャッシュで取得する理由は、以前に取得しておいた別のモジュールが、混ざらないようにするためです。

go.sumは持ち込みマニフェストです: ドキュメントによると、goコマンドは、go.sumにそのファイルのハッシュがないときだけ、チェックサムデータベースに問い合わせます。go.sumにあれば、取得したファイルと照合して、ずれていればセキュリティエラーで止まります。したがって、go.sumが完全なら、エアギャップ環境でsum.golang.orgに届く必要はありません。エアギャップ環境の中で、新しい依存関係を追加しようとするときだけ、問題になります。そのときに使うつまみが、GOSUMDB=off(すでにgo.sumにあるもの以外は検証しません)、GONOSUMDB・GOPRIVATE(パターンに合うモジュールだけ、データベースをスキップします)です。GOINSECUREは、直接取得するときにhttpを許可するだけで、チェックサムの検証をオフにはしません。GONOPROXYは、プロキシを経由せず、直接取得するモジュールのパターンで、デフォルトはGOPRIVATEです。

vendorと-mod: go mod vendorは、依存関係のソースをvendor/にコピーして、vendor/modules.txtを作成します。ビルドフラグ-mod=vendorは、ネットワークもモジュールキャッシュも使わず、vendorだけを見ます。-mod=modは、vendorを無視して、必要ならgo.modを直し、-mod=readonlyは、vendorを無視して、go.modを直す必要があればエラーを出します。go.modのgoバージョンが1.14以上で、vendorディレクトリがあれば、デフォルトが-mod=vendorのように動作します。デフォルトのフラグは、GOFLAGS環境変数で指定します。

現場での姿

このラボのイメージで測ってみた落とし穴が1つあります。イメージの環境に、GOFLAGS=-mod=modが埋め込まれています(ほかのラボがモジュールを自由に取得できるように入れた値です)。その状態で、vendorを作成して、GOPROXY=offでビルドすると、vendorがあるのに、module lookup disabled by GOPROXY=offで失敗しました。-mod=modが、vendorを無視させたからです。GOFLAGS=-mod=vendorを指定するか、GOFLAGSを空にすれば、同じビルドが成功しました。ビルドサーバーのシェルプロファイルやCIの変数に、GOFLAGSが隠れていないかを、まず見る理由です。

規模も測ってみました。rsc.io/quote v1.5.2を取得すると、rsc.io/samplerとgolang.org/x/text(2017年版)がついてきて、モジュールキャッシュのcache/downloadは、4.9MBでした。zip 3つのうち、x/textが4.8MBで、ほとんどすべてです。持ち込み審査に出す一覧は、go.sumの、/go.modが付かない行です。

社内にモジュールプロキシサーバーを置くこともできます。NexusはGoを、proxyとgroupでサポートしていて、hostedは3.93からだと、ドキュメントに書かれています。どちらの場合も、GOPROXYに社内アドレスを書くのは同じです。

次のラボですること

新しいモジュールキャッシュでrsc.io/quote v1.5.2を取得して、go.sumを作成し、そのキャッシュのcache/downloadを/srv/goproxyに移して、GOPROXY=file://で指し示します。外部をふさいだ状態で、新しいキャッシュでビルドできるかを、採点がもう一度ビルドして確認します。vendorを作成して、イメージのGOFLAGSの落とし穴を直接経験して記録したあと、-mod=vendorでビルドします。最後に、go.sumとプロキシの.ziphashを照合した持ち込み記録を書きます。

参考ドキュメント: Go Modules Reference(GOPROXY protocol・Environment variables・Vendoring・Authenticating modules)