TT Lab
开始
学习 学习路径 课程

隔离网络镜像与私有 CA

模块缓存本身就是代理

在 TT Lab 中继续学习

一句话总结

Go 模块应对隔离网络有两条路。把模块代理搭建成文件形式(GOPROXY=file://)和把依赖直接放进仓库(vendor)。无论哪一种,go.sum 都承担导入清单的角色,而一行环境变量(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 环境变量给出。

在现场相遇的样子

在这个实验镜像中实测到一个陷阱。镜像的环境中写死了 GOFLAGS=-mod=mod(这是为了让其他实验能自由下载模块而加入的值)。在这种状态下生成 vendor,再用 GOPROXY=off 构建,即使有 vendor 也会以 module lookup disabled by GOPROXY=off 失败。因为 -mod=mod 使它忽略了 vendor。把它设为 GOFLAGS=-mod=vendor,或者清空 GOFLAGS,同样的构建就成功了。这就是要先看构建服务器的 shell profile 或 CI 变量中是否藏着 GOFLAGS 的原因。

规模也实测过。下载 rsc.io/quote v1.5.2,会附带 rsc.io/sampler 和 golang.org/x/text(2017 年的版本),模块缓存的 cache/download 为 4.9MB。三个 zip 中,x/text 占了 4.8MB,几乎是全部。要提交导入评审的清单,就是 go.sum 中没有附带 /go.mod 的那些行。

内部也可以部署模块代理服务器。文档写明,Nexus 以 proxy 和 group 方式支持 Go,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