問題描述
Viper (本文環(huán)境是Viper 1.1.0)是Go應(yīng)用程序的完整配置解決方案,在很多項目中都有應(yīng)用。etcd是一個分布式KV存儲,最直接的應(yīng)用是配置中心。
Viper除了支持從文件中讀取配置,還支持從遠(yuǎn)程的配置中心讀取配置,使用下面的代碼進(jìn)行配置。
viper.AddRemoteProvider("etcd",
"http://127.0.0.1:2379",
"conf.toml")
viper.SetConfigType("toml")
err := viper.ReadRemoteConfig()
if err != nil {
panic(err)
}
運行后報錯panic: Remote Configurations Error: No Files Found,檢查后發(fā)現(xiàn)etcd開啟了tls,所以需要用https協(xié)議訪問etcd的API,更新代碼如下。
viper.AddSecureRemoteProvider("etcd",
"https://127.0.0.1:2379",
"conf.toml",
"key_path")
viper.SetConfigType("toml")
err := viper.ReadRemoteConfig()
if err != nil {
panic(err)
}
使用AddSecureRemoteProvider方法替換AddRemoteProvider方法,問題依舊。
定位問題
跟蹤源碼發(fā)現(xiàn),最終像etcd發(fā)送請求的是go-etcd包(目前go-etcd已經(jīng)不維護(hù)),在go-etcd的requests.go文件中找到了相關(guān)的源碼,go-etcd調(diào)用了net/http包向etcd發(fā)送請求。

這個時候忽然想到etcd的證書是自簽名的,訪問自簽名證書的https接口應(yīng)該會報錯啊,怎么會請求到內(nèi)容呢?如下圖,在Chrome中訪問etcd的自簽名https接口,會提示證書無效。

我們自己使用go實現(xiàn)一段請求etcd https接口的代碼,看看到底是什么回事,代碼如下。
resp, err := http.Get("https://127.0.0.1:2379/v2/keys/conf.toml?quorum=false&recursive=false&sorted=false")
if err != nil {
// handle error
fmt.Println(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(body))
果然不一樣,我們的代碼會報錯x509: certificate signed by unknown authority,因為是自簽名證書,客戶端無法驗證證書真假,所以這個報錯是可以理解的,go-etcd代碼和我們的測試代碼表現(xiàn)不一致,一定是我們落下了什么,重新梳理go-etcd源碼終于發(fā)現(xiàn)了原因。

在client.go文件的initHTTPSClient方法中,發(fā)現(xiàn)允許繞過證書驗證,這就可以解釋為什么證書無效也能獲取到數(shù)據(jù)了,繞過了證書的驗證環(huán)節(jié),相當(dāng)于不管證書真假都拿來用?,F(xiàn)在可以解釋使用AddRemoteProvider方法訪問https接口為什么可以獲取到內(nèi)容,但是無法解釋AddSecureRemoteProvider方法為什么無法從https接口獲取內(nèi)容,因為兩個方法在發(fā)送http請求階段的代碼是一致的,都忽略了證書驗證。
查看AddSecureRemoteProvider的注釋,發(fā)現(xiàn)了原因。

原來...AddSecureRemoteProvider這個Secure指的并不是使用安全鏈接https,而是在請求到內(nèi)容后加了一個解密的步驟(Secure指請求的是加密過的內(nèi)容,而不是使用加密鏈接請求),最后一個參數(shù)接收的也并不是客戶端證書,而是解密的gpg key... 根據(jù)viper的文檔,這個gpg key是可選的,我們這個例子中,如果給gpg key傳入一個空字符串,也是可以正常執(zhí)行的...
必須吐槽一下viper的命名,哪里是AddSecureRemoteProvider,明明應(yīng)該叫AddEncryptedRemoteProvider
總結(jié)
出現(xiàn)這個問題,主要是誤會了AddSecureRemoteProvider接口表達(dá)的意思,并且go-etcd允許忽略證書驗證,也讓問題變得更加離奇。
當(dāng)然go-etcd的這種配置是非常合理的,內(nèi)部系統(tǒng)使用自簽名證書是一個很正常的行為。