1. 簡介
為了確保一致性構建,Go引入了go.mod文件來標記每個依賴包的版本,在構建過程中go命令會下載go.mod中的依賴包,下載的依賴包會緩存在本地,以便下次構建。
考慮到下載的依賴包有可能是被黑客惡意篡改的,以及緩存在本地的依賴包也有被篡改的可能,單單一個go.mod文件並不能保證一致性構建。
為了解決Go module
的這一安全隱患,Go開發團隊在引入go.mod
的同時也引入了go.sum
文件,用於記錄每個依賴包的哈希值,在構建時,如果本地的依賴包hash值與go.sum文件中記錄得不一致,則會拒絕構建。
本節暫不對模塊校驗細節展開介紹,只從日常應用層面介紹:
- go.sum 文件記錄含義
- go.sum文件內容是如何生成的
- go.sum是如何保證一致性構建的
2. go.sum文件記錄
go.sum文件中每行記錄由module
名, 版本和哈希組成,並由空格分開,格式如下
<module> <version>[/go.mod] <hash>
比如某個go.sum
文件記錄中記錄了github.com/google/uuid
這個依賴的v.1.1
版本的哈希值:
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
在Go module
機制下,我們需要同時使用依賴包的名稱和版本才可以准確的描述一個依賴,為了方便敘述,下面我們使用依賴包版本來指代依賴包名稱和版本。
正常情況下,每個依賴包版本會包含兩條記錄:
- 第一條記錄為該依賴包版本整體(所有文件)的哈希值,
- 第二條記錄僅表示該依賴包版本中go.mod文件的哈希值
如果該依賴包版本沒有go.mod
文件,則只有第一條記錄。如上面的例子中,v1.1.1表示該依賴包版本整體,而v1.1.1/go.mod
表示該依賴包版本中go.mod文件。
依賴包版本中任何一個文件(包括go.mod
)改動,都會改變其整體哈希值
,此處再 **額 外記錄依賴包版本 **的go.mod
文件主要用於計算依賴樹時不必下載完整的依賴包版本,只根據go.mod即可計算依賴樹。
每條記錄中的哈希值前均有一個表示哈希算法的h1:,表示后面的哈希值是由算法SHA-256計算出來的,自Go module從v1.11版本初次實驗性引入,直至v1.14 ,只有這一個算法。
go.sum
文件中記錄的依賴包版本數量往往比go.mod
文件中要多,這是因為二者記錄的粒度不同導致的。
go.mod
只需要記錄直接依賴的依賴包版本,只在依賴包版本不包含go.mod
文件時候才會記錄間接依賴包版本
go.sum
則是要記錄構建用到的所有依賴包版本。
3. 生成
假設我們在開發某個項目,當我們在GOMODULE模式下引入一個新的依賴時,通常會使用go get命令獲取該依賴,比如:
require (
github.com/google/uuid v1.0.0
)
go.sum
文件中則會記錄依賴包的哈希值(同時還有依賴包中go.mod的哈希值),如:
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
值得一提的是,在更新go.sum
之前,為了確保下載的依賴包是真實可靠的,go命令在下載完依賴包后還會查詢GOSUMDB
環境變量所指示的服務器,以得到一個權威的依賴包版本哈希值。如果go命令計算出的依賴包版本哈希值與GOSUMDB
服務器給出的哈希值不一致,go命令將拒絕向下執行,也不會更新go.sum文件。
go.sum存在的意義在於,我們希望別人或者在別的環境中構建當前項目時所使用依賴包跟go.sum中記錄的是完全一致的,從而達到一致構建的目的。
4.校驗
假設我們拿到某項目的源代碼並嘗試在本地構建,go命令會從本地緩存中查找所有go.mod中記錄的依賴包,並計算本地依賴包的哈希值,然后與go.sum中的記錄進行對比,即檢測本地緩存中使用的依賴包版本是否滿足項目go.sum文件的期望。
如果校驗失敗,說明本地緩存目錄中依賴包版本的哈希值和項目中go.sum中記錄的哈希值不一致,go命令將拒絕構建。
這就是go.sum存在的意義,即如果不使用我期望的版本,就不能構建。
當校驗失敗時,有必要確認到底是本地緩存錯了,還是go.sum記錄錯了。
需要說明的是,二者都可能出錯,本地緩存目錄中的依賴包版本有可能被有意或無意地修改過,項目中go.sum中記錄的哈希值也可能被篡改過。
當校驗失敗時,go命令傾向於相信go.sum,因為一個新的依賴包版本在被添加到go.sum前是經過GOSUMDB(校驗和數據庫)驗證過的。此時即便系統中配置了GOSUMDB(校驗和數據庫),go命令也不會查詢該數據庫。
5.校驗和數據庫
環境變量GOSUMDB
標識一個checksum database
,即校驗和數據庫,實際上是一個web服務器,該服務器提供查詢依賴包版本哈希值的服務。
該數據庫中記錄了很多依賴包版本的哈希值,比如Google官方的sum.golang.org
則記錄了所有的可公開獲得的依賴包版本。
除了使用官方的數據庫,還可以指定自行搭建的數據庫,甚至干脆禁用它(export GOSUMDB=off
)。
如果系統配置了GOSUMDB,在依賴包版本被寫入go.sum
之前會向該數據庫查詢該依賴包版本的哈希值進行二次校驗,校驗無誤后再寫入go.sum
。
如果系統禁用了GOSUMDB
,在依賴包版本被寫入go.sum
之前則不會進行二次校驗,go命令會相信所有下載到的依賴包,並把其哈希值記錄到go.sum
中。