gomod 冷门特性

Go mod’s lesser known features #

Modules 是 Go 语言的依赖管理方式。每个 module 是众多已经发行并标注版本的 package 集合。module 里的每个 package 是在同一个目录下并一起编译的源码文件集合。

本文将探索 Go Modules 的设计,学习其如何支持供应链安全。go mod 官方文档: https://golang.org/ref/mod

剖析 go.mod 文件 #

1
2
3
4
5
6
7
8
// Sample go.mod file
module github.com/org/module         // module name

require (                            // module dependencies
  github.com/foo/bar  v0.1.2
  github.com/cow/moo  v1.2.3
  mydomain.com/gopher v0.2.3-beta1
)

后面将会讨论 Go 的 module 命名限制。项目中标注的版本是特定的最小依赖版本。注意,这里不支持版本范围,仅支持单个语义化的版本。当 Go 处理依赖树时,它将会选择发现的最大版本。如此,MVS 可以无锁创建可再现的依赖树(保证编译的幂等)。

Minimum Version Selection(MVS) #

最小版本选择:https://research.swtch.com/vgo-mvs Go最小版本: https://go.dev/ref/mod#minimal-version-selection

Golang 使用 MVS (最小版本选择) 算法来选择依赖版本。对于重现构建和避开 NP-complete 运行复杂性这个稳定算法有不错的性能。由于依赖管理问题的特点(constrained)所以没必要使用 SAT 求解计算。

简言之,MVS 核心是一个广度优先的 moduel 版本遍历方式。下图是由依赖、依赖的依赖以及更深层次的依赖树构成

https://go.dev/doc/mvs/buildlist.svg

这里的最简无锁实现的思想决定了依赖管理系统的稳定性(可重现)。

go.mod 中的指令 #

go.mod 文件有许多控制版本和依赖的指令。

1
2
3
4
5
6
7
8
9
module example.com/my/thing

go 1.16

require example.com/other/thing v1.0.2
require example.com/new/thing/v2 v2.3.4
exclude example.com/old/thing v1.2.3
replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5
retract [v1.9.0, v1.9.5]

go - 设置最小 Go 语法版本 require - 制定特定的 module 依赖 exclude - 被排除的依赖版本 replace - 不更改项目内的应用关系,替换底层依赖包 retract - 此 module 的小版本号和补丁版本好(major.minor.patch)

Deprecated 用来备注在 modules 中可以使用主版本。添加一个注释并标记一个新的版本。

1
2
// Deprecated: use example.com/mod/v2 instead.
module example.com/mod

As of Go 1.17, there are two require blocks, one for direct and indirect each, to support lazy loading. https://go.dev/ref/mod#lazy-loading

环境变量 #

Go supports a number of environment variables for controlling how modules and module aware commands 4 work. The following table contains the variables referenced in later sections. For a full list, more details, and examples, see: 5 6

You can use go env to see the current settings.

variable used for
GOMODCACHE module 相关文件目录
GOPRIVATE module 处理的私有依赖,配置的路径将绕开 proxy,跳过校验检查
GOPROXY module 代理的次序清单
GONOPROXY 绕开代理的依赖
GOSUMDB 配置使用哪个校验服务器和公钥来做依赖包的校验,有序
GONOSUMDB 设置不做校验的代码仓库
GOVCS sets VCS tools allowed for public and private access
GOINSECURE globs to allow fallback to http

Hashes 和 the go.sum file #

go 命令下载 module 时,计算一个加密散列并和一个已知的值做对比,验证此文件自其首次以来没有被篡改。Modules 在 go.sum 文件中存储这些散列用以匹配验证。go 同样也在 module 缓存中存储这些散列,并将其与全局数据库做对比。

https://go.dev/ref/mod#authenticating

module 本地缓存 #

Go 在本地系统中维护一个共享的 module 缓存。这里就是 modules 下载和存储的场所。本地缓存路径由 GOMODCACHE 变量决定。Module 代码是只读的,为了阻止本地更改,确保只在我的机器工作。共享缓存同样也包含预先构建的组建。种种这些本机的多个项目可以复用相同的依赖包。

Global services modules and hashes #

The Go team maintain global proxies for sumdb, cachedb, and global hash integrity and revocation checks. 10 The checksum database can be used to detect misbehaving origin and proxy servers. It has a merkel tree transparency log for hashes powered by the Trillian project. The cache database proxies public modules and will maintain copies even if the origin server removes them.

The Go team has taken privacy seriously. These services record very minimal information. You can read the privacy statemnt for sum.golang.org/privacy for details. Their communications in issues on GitHub reflects this. For example, only limited auth features have been enabled, because they are being careful in trying to maintain privacy in proxy.

Module naming #

Go 拥有一些命名规则。这部分设计,使用代码主机而不是包注册表,但也是出于安全原因。

module 第一部分要求是一个域名

The domain requirement is itself required because Go resolves modules to the code host. It also prevents a class of dependency confusion, discussed in the next section.

只包含 ascii 字符、数字和被限制的标点符号 ([.~_-])

限制允许的导入路径参数可以防止同形或同形攻击

1
2
$ go mod init ɢoogle.com/chrome
go: malformed module path "ɢoogle.com/chrome": invalid char 'ɢ'

不可以斜杠或逗号开头或结尾 Slash and dot restrictions prevent absolute and relative path from being part of imports. While this means they are more verbose, it also means that

  1. you can always see the exact package being used
  2. relative and absolute path attacks are not possible

There are more restrictions

For specific contexts, there are more rules

The domain part has further restrictions Windows has reserved files to avoid Major version suffixes See: https://golang.org/ref/mod#go-mod-file-ident ↩︎ for more details.

via:https://verdverm.com/go-mods/