一個可運行的軟件項目通常包括兩個要素:代碼和密鑰。我們通常會使用無版本控制的FTP和有版本控制的SVN、git等成熟的工具進行代碼管理;而在我參加的大大小小、許許多多的項目中,密鑰管理似乎缺乏成熟或標準的實踐。本文將歷數一下筆者在各個使用過的密鑰管理實踐并分析他們的優缺點。最后給大家推薦一款密鑰管理工具:vault。
在軟件項目開發中,密鑰常常應用于下面四個場景:
- 本地開發:通常包括開發環境的數據庫密碼、用于訪問第三方API的token、一些私有程序包倉庫的憑證等。
- CICD流水線:比如Push Docker鏡像的Docker倉庫的訪問憑證、用于部署的云服務憑證(AWS Secret等)、用于訪問K8S集群的token等
- 運行線上服務:線上服務啟動所需的數據庫密碼、API Token等等,同時可能需要管理用于多套環境的不同密鑰。
- 線上運維:線上發生事故時,需要在本地登入堡壘機(跳板機)的SSH Key或集群的訪問憑證。
本地開發:“薪火相傳”的密鑰文件
當我們加入一個團隊時,通常會有一個Readme文檔告訴你項目代碼庫的下載鏈接。除此之外它會告訴你需要向團隊“前輩”索要密鑰文件,不然你的代碼是不能在本地啟動的。同時有人告訴你,這個密鑰文件千萬不要加入到git倉庫中。
這種“薪火相傳”的密鑰管理方式,是最原始也是最常見的方式。它常常會伴隨這樣幾個問題:
- 密鑰更換或者引入新的密鑰后,團隊其它成員因為沒有得到最新的密鑰文件,導致服務在本地起不來。
比如你會聽到這樣的對話:
- A: “我拉了一下最近的代碼,怎么就跑不起來了?”
- 坐在旁邊的B突然想起了什么:“好吧,我想起來了!我改了一下數據庫密碼,忘記告訴你了,我把最新的密鑰發給你?!被蛘摺拔倚录恿艘粋€功能因為使用API-KEY要訪問消息隊列,我在自己本地的環境變量里面加上了這個KEY,忘記告訴你們了”
- 隨后B把最新的密鑰文件傳給了A。幾天后,同在項目的C也遇到了同樣的問題……
- 誤提交到代碼倉庫問題:
相信已經不止一次地聽人提醒:千萬不要將密鑰文件明文提交到git。但是密鑰泄露在代碼倉庫的問題依舊時有發生。
本地開發:將密鑰加密后存放在Git倉庫
密鑰和代碼一樣,在團隊項目中同樣需要進行共享、同步。密鑰放在git倉庫中本來是可以解決團隊協作問題的,只不是不能被明文存儲。那么,如果是將密鑰加密后再提交到git倉庫呢?
git-crypt便是這樣一款可將git倉庫中的密鑰文件進行透明加密和解密的工具。它可以將密鑰文件在push時加密,在pull下來后解密。更多介紹和使用說明可以參考:https://github.com/AGWA/git-crypt。
借助git版本控制工具,它可以實現:
- 使用git進行密碼共享
- 密鑰的版本控制
- 用戶權限管理
問題:
- 密碼可能在多個服務中使用,怎么同步?
持續集成流水線中的密鑰管理
在現在的Web項目的CI/CD流程中,通常會將項目代碼經過構建打包生成docker鏡像(制品);在部署階段,不同環境會采用相同的docker鏡像,但是會使用不同的環境變量(比如集群、域名、數據庫地址密碼等)傳入到docker的運行時,從而完成在不同環境的部署。
環境(變量)在不同的CI/CD中有不同形式,比如的Jenkins的Credential、GoCD的Environment、CircleCI的Context。
如果將所有的部署與運行時所需要的密鑰數據都保存到pipeline上,會導致下面的問題
- 過多的密碼字段,將密碼作為環境變量一個個傳遞到服務十分復雜
- pipeline存環境變量一般加密后難以解密,如果你設置完自己都忘記了,那這個環境就徹底忘了
解決的辦法一般是在pipeline上保存盡量少的密鑰字段,我們通過一次認證就可以具備獲取所有密鑰數據的權限。
密碼即服務:Hashicorp Vault
在云和基礎設施自動化時代,我們應該知道一家名為Hashcorp的公司,其代表作有知名的terraform、consul、packer、vagrant。vault也是這家公司的產品之一,它通過API將密碼以服務的方式暴露出去。它可以提供:
- 中心化的密碼服務
- 更安全的加密存儲
- 密碼的服務化
- 豐富的第三方集成:實現認證的擴展、多平臺密鑰管理
服務化后的vault可以做到
- 與Github身份認證集成,比如你可以做到只允許在特定git organization下的用戶才能獲取密鑰
- 簽發臨時的SSH證書:比如你只允許一個30分鐘內有效的SSH KEY來登錄堡壘機
- 生成臨時的AWS KEY:比如你只能用一個30分鐘內有效的AWS憑證
- 定期更換數據庫密碼,因為數據庫長期不更換會加大泄露的風險
- OTP:基于時間的臨時密碼
- 密碼權限策略:只允許特定的微服務讀取或者寫入指定的密鑰
- 密碼的revoke(同事下項目了怎么辦?)
項目實踐
- 不在本地持久化存儲密鑰,每次獲取密鑰應該通過腳本實時獲取并保存在console的會話級的環境變量里。
- 密鑰是有時效,定期輪換
- 密鑰獲取者是有身份的