Golang基于Vault實現(xiàn)敏感信息保護
背景
在應(yīng)用程序的配置中,有一類信息比較敏感,比如數(shù)據(jù)庫的用戶名/密碼、云平臺的 AK/SK、各種 API keys、各類賬號/密碼等,這些信息的泄露會帶來嚴重的安全問題。
然而在實際生產(chǎn)活動中,這些敏感信息的管理有很大的漏洞,存在很大的泄露風險:
- 代碼或配置以明文形式記錄敏感信息,存放在代碼倉庫中,甚至誤上傳到 GitHub;
- 敏感信息的生成、分發(fā)、保管、部署全流程經(jīng)多人之手,缺乏有效的管控手段;
- 敏感信息生成之后長期有效,沒有自動輪轉(zhuǎn)機制,加大了泄露風險及影響程度。
敏感信息保護是網(wǎng)絡(luò)安全工作的一個重要部分。
敏感信息保護
敏感信息保護是一個比較復(fù)雜的系統(tǒng)性工作,主要包括以下幾個部分:
- 要有一個專門的平臺來托管敏感信息,本文采用 HashiCorp 公司開源的 Vault 工具
- 應(yīng)用程序要與該平臺集成,從平臺獲取敏感信息,并完成續(xù)租和輪轉(zhuǎn)等操作
- 部署工具要與該平臺集成,為應(yīng)用程序注入登錄平臺所需的身份憑據(jù)
Vault 是一個強大的敏感信息管理工具,自帶了多種認證引擎和密碼引擎,并通過插件機制允許自定義引擎,可應(yīng)用于多種常見的敏感信息保護場景,具體用法本文不做介紹,請參考Vault官方文檔。至于部署發(fā)布工具與 Vault 的集成,與所使用的部署工具及發(fā)布流水線有關(guān),每個公司不盡相同,本文也不做詳細展開。
本文主要探討應(yīng)用程序與 Vault 的集成,以數(shù)據(jù)庫憑據(jù)為例,介紹應(yīng)用程序如何安全地從 Vault 獲取敏感信息,并進一步實現(xiàn)自動輪轉(zhuǎn)。
應(yīng)用集成方案
應(yīng)用程序與 Vault 的集成可以采用直接方式,即開發(fā)者自行編寫代碼實現(xiàn)登錄認證、Token 續(xù)租、過期再登錄以及敏感信息的獲取、續(xù)租和輪轉(zhuǎn)等邏輯,這種集成方式對應(yīng)用程序有較多的代碼侵入,實現(xiàn)成本較高。
Vault 官方提供了一種對應(yīng)用程序代碼低侵入甚至無侵入的集成方案,即 Vault Agent,它實現(xiàn)了與 Vault Server 的所有交互邏輯,并且還可以通過模板功能將獲取的敏感信息渲染成本地配置文件,應(yīng)用程序只需要讀取該配置文件即可。
本文采用基于 Agent 的間接集成方案:Agent 負責登錄 Vault 并管理 Token 續(xù)租及過期再登錄,根據(jù)配置模板文件從 Vault 獲取所需的敏感信息,渲染成本地配置文件,管理敏感信息的續(xù)租及輪轉(zhuǎn),并更新本地配置文件;應(yīng)用程序只需讀取本地配置文件獲取敏感信息,并持續(xù)監(jiān)聽該文件,當文件變化時進行動態(tài)更新。這是一種完全解耦的間接集成方式,如下圖所示。
準備工作
1. 創(chuàng)建具有 CRUD 權(quán)限的數(shù)據(jù)庫角色
# 首先啟用數(shù)據(jù)庫密碼引擎 $ vault secrets enable database # 創(chuàng)建 MySQL 數(shù)據(jù)庫配置 $ export MYSQL_URL=x.x.x.x:3306 $ vault write database/config/mydb \ ????plugin_name=mysql-database-plugin \ ????connection_url="{{username}}:{{password}}@tcp($MYSQL_URL)/" \ ????allowed_roles="mydb-crud" \ ????username="root" \ ????password="******" # 說明:該用戶需要具有用戶管理權(quán)限,此處直接使用 root # 創(chuàng)建 mydb-crud 角色(具有增刪改查完整權(quán)限) $ vault write database/roles/mydb-crud \ ????db_name=mydb \ ????creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}'; GRANT SELECT, INSERT, DELETE, UPDATE ON mydb.* TO '{{name}}'@'%';" \ ????default_ttl="2m" \ ????max_ttl="10m" # 說明:為方便測試,此處 TTL 設(shè)置較小,實際使用時需要評估合理的值 # 測試獲取 mydb-crud 角色的憑據(jù),并查看驗證 $ vault read database/creds/mydb-crud $ vault list sys/leases/lookup/database/creds/mydb-crud
2. 創(chuàng)建具有上述數(shù)據(jù)庫權(quán)限的 AppRole
# 啟用 Approle 認證引擎 $ vault auth enable approle # 創(chuàng)建權(quán)限策略 mydb-policy $ vault policy write mydb-policy -<<EOF #獲取憑據(jù)的權(quán)限 path "database/creds/mydb-crud" { ??capabilities = [ "read" ] } #續(xù)租憑據(jù)的權(quán)限 path "sys/leases/+/database/creds/mydb-crud/*"?{ ? capabilities = [ "update" ] } EOF # 創(chuàng)建具有 mydb-policy 權(quán)限的 AppRole $ vault write auth/approle/role/myapp token_policies="mydb-policy" \ ????token_ttl=2m token_max_ttl=10m # 查看創(chuàng)建的 AppRole $ vault read auth/approle/role/myapp
3. 獲取 AppRole 身份憑據(jù)( RoleID 和 SecretID )
# 獲取 RoleID $ vault read -field=role_id auth/approle/role/myapp/role-id >~/.roleid # 獲取 SecretID $vault write -f -field=secret_id auth/approle/role/myapp/secret-id >~/.secretid
然后由部署發(fā)布工具將 RoleID 和 SecretID 注入到應(yīng)用程序所在服務(wù)器的約定位置文件中。
登錄認證
Agent 的 Auto_Auth 功能實現(xiàn)了登錄認證、Token 續(xù)租和過期再登錄等邏輯,允許指定認證方法和 Token 保存位置。這里采用 AppRole 認證,需要指定 RoleID 和 SecretID 兩個文件的位置(由部署發(fā)布工具注入)。
auto-auth 配置塊如下所示:
auto_auth { method { type = "approle" config = { role_id_file_path = "/vault/config/approle/roleid" secret_id_file_path = "/vault/config/approle/secretid" remove_secret_id_file_after_reading = false } } sink "file" { config = { path = "/tmp/.vault-token-via-agent" } } }
獲取數(shù)據(jù)庫憑據(jù)
Agent 的 Template 功能可以根據(jù)指定位置的模板文件獲取所需的敏感信息,填充、渲染成配置文件,保存在指定位置,當渲染出的結(jié)果文件發(fā)生變化時還可以執(zhí)行給定的命令。
template 相關(guān)的配置塊如下所示:
template_config { exit_on_retry_failure = true } template { error_on_missing_key = true source = "/vault/config/appconf/config.ctmpl" destination = "/vault/config/appconf/config.yaml.tmp" exec { command = ["dd", "if=/vault/config/appconf/config.yaml.tmp", "of=/vault/config/appconf/config.yaml" ] timeout = "5s" } }
配置模板文件config.ctmpl
通過模板語言指定敏感信息的占位符及獲取路徑,經(jīng) Agent 渲染后生成應(yīng)用程序能識別的配置文件config.yaml
。配置模板文件的相關(guān)片段如下所示:
# config.ctmpl database: mysql: {{- with secret "database/creds/mydb-crud" }} username: {{ .Data.username }} password: {{ .Data.password }} {{- end }} address : x.x.x.x:3306 dbname : mydb options : charset=utf8mb4&parseTime=True&loc=Local
應(yīng)用程序讀取本地配置文件config.yaml
即可獲取數(shù)據(jù)庫憑據(jù),不需要與 Vault 進行交互,實現(xiàn)了與 Vault 的完全解耦。
使用數(shù)據(jù)庫憑據(jù)
關(guān)于 Golang 應(yīng)用程序如何讀取配置文件可以參考《淺談Golang配置管理》這篇文章。
這里僅給出使用數(shù)據(jù)庫憑據(jù)相關(guān)的代碼片段,如下:
var ( mysqlUsername string mysqlPassword string mysqlAddress string mysqlDBname string mysqlOptions string ) func initConfig() { mysqlUsername = Config.Database.MySQL.Username mysqlPassword = Config.Database.MySQL.Password mysqlAddress = Config.Database.MySQL.Address mysqlDBname = Config.Database.MySQL.DBname mysqlOptions = Config.Database.MySQL.Options } func connectMySQL() (*gorm.DB, error) { msyqlDSN := fmt.Sprintf("%s:%s@tcp(%s)/%s?%s", mysqlUsername, mysqlPassword, mysqlAddress, mysqlDBname, mysqlOptions) return gorm.Open(mysql.Open(msyqlDSN), &gorm.Config{}) }
數(shù)據(jù)庫憑據(jù)自動輪轉(zhuǎn)
Agent 從 Vault 獲取數(shù)據(jù)庫憑據(jù)后,會在其TTL
到期前進行續(xù)租,當因Max-TTL
限制無法續(xù)租時,會自動輪轉(zhuǎn),重新獲取一組新的憑據(jù),并更新在本地配置文件中。
應(yīng)用程序監(jiān)聽到本地配置文件的變化時,需要讀取新的憑據(jù),并進行動態(tài)加載。配置動態(tài)更新的具體方法可以參考《淺談Golang配置管理》這篇文章。
這里僅給出配置動態(tài)加載相關(guān)的示例代碼,如下:
var db *gorm.DB var dbLocker sync.Mutex func reconnectMySQL() { // get new mysql creds creds := getNewMySQLCreds() if creds.Username == mysqlUsername && creds.Password == mysqlPassword { log.Println("MySQL creds not changed, skip mysql reconnection.") return } dbLocker.Lock() defer dbLocker.Unlock() // re-connect mysql with new creds mysqlUsername = creds.Username mysqlPassword = creds.Password d, err := connectMySQL() if err != nil { log.Println("MySQL connect failed:", err) return } // setupDatabase(d) db = d }
說明:上述示例代碼通過重建gorm.DB
對象來更新數(shù)據(jù)庫憑據(jù),是一種可行的方式,但是比較粗暴,會導(dǎo)致連接重建,在業(yè)務(wù)高峰期時可能會影響服務(wù)性能,在生產(chǎn)上建議尋求更優(yōu)雅、平滑的實現(xiàn)方式。大家有好的實現(xiàn)或思路可以在評論區(qū)留言分享。
總結(jié)
本文探討了基于 Vault 的敏感信息保護方案,重點介紹了應(yīng)用程序通過 Agent 與 Vault 間接集成的方法,以數(shù)據(jù)庫憑據(jù)為例,具體說明了應(yīng)用程序如何安全地從 Vault 獲取敏感信息,并進一步實現(xiàn)自動輪轉(zhuǎn)。
以上就是Golang基于Vault實現(xiàn)敏感信息保護的詳細內(nèi)容,更多關(guān)于Go Vault敏感信息保護的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺析Go中函數(shù)的健壯性,panic異常處理和defer機制
這篇文章主要為大家詳細介紹了Go中函數(shù)的健壯性,panic異常處理和defer機制的相關(guān)知識,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2023-10-10淺析Golang開發(fā)中g(shù)oroutine的正確使用姿勢
很多初級的Gopher在學習了goroutine之后,在項目中其實使用率不高,所以這篇文章小編主要來帶大家深入了解一下goroutine的常見使用方法,希望對大家有所幫助2024-03-03Golang?中的?strconv?包常用函數(shù)及用法詳解
strconv是Golang中一個非常常用的包,主要用于字符串和基本數(shù)據(jù)類型之間的相互轉(zhuǎn)換,這篇文章主要介紹了Golang中的strconv包,需要的朋友可以參考下2023-06-06Go json omitempty如何實現(xiàn)可選屬性
在Go語言中,使用`omitempty`可以幫助我們在進行JSON序列化和反序列化時,忽略結(jié)構(gòu)體中的零值或空值,本文介紹了如何通過將字段類型改為指針類型,并在結(jié)構(gòu)體的JSON標簽中添加`omitempty`來實現(xiàn)這一功能,例如,將float32修改為*float322024-09-09Go語言如何實現(xiàn)將[][]byte轉(zhuǎn)為io.Reader
本文主要介紹了如何在Go語言中實現(xiàn)將[][]byte轉(zhuǎn)換為io.Reader,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2025-02-02