使用 Vault 加密的 GITLAB CICD Pipeline
本文的的核心內容是提供有關如何設置 Gitlab 和 Vault 以在 CI/CD 管道構建期間使用密鑰。另外,本文將分解 JWT 授權過程,并解釋 Gitlab + Vault 的流程。通過本文,讀者最終可以實現自己的帶有 Vault 密鑰的 CI/CD 流程。
目的
在 Vault 上啟用 JWT 身份驗證方法
利用 JWT 授權從 Vault 中讀取密鑰以進行 Gitlab CI/CD 作業
背景
什么是 Gitlab?
GitLab 是一個基于 Web 的 DevOps 生命周期工具 ,它提供了一個 Git 代碼存儲庫管理,同時它使用 GitLab Inc 開發的開源許可證提供 wiki、問題跟蹤以及持續集成和pipeline的功能。
在學習Gitlab的定義之前,首先需要了解一些術語??赡苣洺S龅较馟it,Gitlab,GitHub和Bitbucket這樣的術語。
Git - 它是一個源代碼版本控制系統,可讓您在本地跟蹤更改并從遠程資源推送或提取更改。 GitLab ,GitHub和Bitbucket - 提供遠程訪問Git存儲庫的服務。除了托管代碼之外,這些服務還提供用來幫助管理軟件開發生命周期的附加功能。這些附加功能包括管理不同人之間的代碼共享,錯誤跟蹤,wiki空間和其他“社交編碼”工具。
- GitHub 是一項公開可用的免費服務,它要求所有代碼(除非您有付費帳戶)公開。任何人都可以看到您推送給GitHub的代碼并提供改進建議。GitHub目前承載數以萬計的開源項目的源代碼。
- GitLab是一種類似github的服務,組織可以使用它來提供git存儲庫的內部管理。它是一個自我托管的Git-repository管理系統,可以保持用戶代碼的私密性,并且可以輕松地部署代碼的更改。
GitLab是集中服務器上管理git存儲庫的一個好方法。GitLab讓您可以完全控制您的存儲庫或項目,并允許您自己決定是公共還是私有。
什么是HashiCorp Vault?
在企業級應用開發過程中,團隊每時每刻都需要管理各種各樣的私密信息,從個人的登陸密碼、到生產環境的SSH Key以及數據庫登錄信息、API認證信息等。通常的做法是將這些秘密信息保存在某個文件中,并且放置到git之類的源代碼管理工具中。個人和應用可以通過拉取倉庫來訪問這些信息。但這種方式弊端很多,比如跨團隊分享存在安全隱患、文件格式難以維護、私密信息難以回收等。
尤其是微服務大行其道的今天,如何讓開發者添加私密信息、應用程序能輕松的獲取私密信息、采用不同策略更新私密信息、適時回收私密信息等變得越來越關鍵。所以企業需要一套統一的接口來處理私密信息的方方面面,而HashiCorp Vault就是這樣的一款工具。
Vault 是 hashicorp 推出的 secrets 管理、加密即服務與權限管理工具。
它提供了這些功能:
- 集中管理各種私密信息;
- 為私密信息設置租期(Lease),到期后自動失效;
- 密鑰的動態生成、注銷和滾動更新;
- 動態創建無需保存的一次性登錄密鑰;
- 作為數據加密/解密接口;
- 完整的審計記錄;
- 命令行 以及 RESTful API 訪問接口;
Valut存儲私密信息
不僅可以存放現有的私密信息,還可以動態生成用于管理第三方資源的私密信息。所有存放的數據都是加密的。任何動態生成的私密信息都有租期,并且到期會自動回收。
滾動更新秘鑰
用戶可以隨時更新存放的私密信息。Vault提供了加密即服務(encryption-as-a-service)的功能,可以隨時將密鑰滾動到新的密鑰版本,同時保留對使用過去密鑰版本加密的值進行解密的能力。對于動態生成的秘密,可配置的最大租賃壽命確保密鑰滾動易于實施。
審計日志
保管庫存儲所有經過身份驗證的客戶端交互的詳細審核日志:身份驗證,令牌創建,私密信息訪問,私密信息撤銷等。可以將審核日志發送到多個后端以確保冗余副本。
另外,HaishiCorp Vault提供了多種方式來管理私密信息。用戶可以通過命令行、HTTP API等集成到應用中來獲取私密信息。HashiCorp Vault也能與Ansible、Chef、Consul等DevOps工具鏈無縫結合使用。
什么是 CI/CD?
CI/CD 自動化了將代碼從開發人員機器交付到生產環境的過程。雖然只有短短數語,但如上圖所示,該流程本質上包含很多步驟的。本節將詳細描述該過程。CI/CD 代表持續集成和持續部署,正如上面縮寫以及圖所示,這是兩個不同的階段。持續集成是集成代碼更改的過程,驗證新代碼更改仍然可以構建/編譯應用程序,并確保新代碼通過一組測試。
縮略詞 CI / CD 具有幾個不同的含義。CI/CD 中的"CI"始終指持續集成,它屬于開發人員的自動化流程。成功的 CI 意味著應用代碼的新更改會定期構建、測試并合并到共享存儲庫中。該解決方案可以解決在一次開發中有太多應用分支,從而導致相互沖突的問題。
CI/CD 中的"CD"指的是持續交付和/或持續部署,這些相關概念有時會交叉使用。兩者都事關管道后續階段的自動化,但它們有時也會單獨使用,用于說明自動化程度。
持續交付通常是指開發人員對應用的更改會自動進行錯誤測試并上傳到存儲庫(如 GitHub 或Gitlab),然后由運維團隊將其部署到實時生產環境中。這旨在解決開發和運維團隊之間可見性及溝通較差的問題。因此,持續交付的目的就是確保盡可能減少部署新代碼時所需的工作量。
例如,假設您有一個用 GoLang 編寫的 Web 應用程序。作為開發人員,您對本地開發機器上的現有應用程序進行一些更改,并將更改推送到 Gitlab。接下來,Gitlab 將嘗試使用您的更改編譯現有代碼庫。假設編譯成功,Gitlab 將對新編譯的代碼進行多次測試,以確保應用程序按預期運行。如果測試成功,開發人員可以將更改合并到 MAIN 分支中。
現在你可能會問如果這個階段不成功會發生什么?使用上面的示例,假設您初始化了一個未使用的變量。如果您是 GoLang 開發人員,您已經知道這將無法編譯,但對于本示例,假設代碼已推送到 Gitlab。Gitlab 將再次嘗試編譯包含您的更改的代碼。但是,編譯將失敗,并且通常管道將在第一次出現錯誤時停止運行。Gitlab 將為開發人員提供審查產生的錯誤的能力。在解決此問題之前,Gitlab 將不允許合并新代碼。
持續部署是再次評估/測試新提交的代碼的過程,將應用程序推送給 QA 以進行進一步評估,最后在人工交互后將代碼推送到生產環境。推送到產線(生產)意味著將您的代碼推送到環境中,以便用戶可以使用您的新代碼。同樣,正如上面的圖表所示,此過程還有更多內容,但希望讀者能夠去了解更多的關于CICD的過程。
了解 JWT 授權
JSON Web Token (JWT)是目前最流行的跨域身份驗證解決方案。簡單說,OAuth 就是一種授權機制。數據的所有者告訴系統,同意授權第三方應用進入系統,獲取這些數據。系統從而產生一個短期的進入令(token),用來代替密碼,供第三方應用使用。
傳統的授權認證方式,需要持久化session數據,寫入數據庫或文件持久層等,且授權校驗依賴于數據持久層。這樣的方式,對于結構維護成本大,實現單點登錄較為復雜,且沒有分布式架構,無法支持橫向擴展,風險較大(如果持久層失敗,整個認證體系都會掛掉)。
JWT則無須持久化會話數據,是以加密簽名的方式實現了用戶身份認證授權,很好的解決了跨域身份驗證,分布式session共享、單點登錄和橫向擴展等問題。另外,需要注意JWT:
- JWT默認不加密,但可以加密。生成原始令牌后,可以使用改令牌再次對其進行加密。
- JWT不僅可用于認證,還可用于信息交換。善用JWT有助于減少服務器請求數據庫的次數。
- JWT的最大缺點是服務器不保存會話狀態,一旦JWT簽發,在有效期內將會一直有效。
- JWT的有效期不宜設置太長,認證信息,因此一旦信息泄露,任何人都可以獲得令牌的所有權限。
- 為了減少JWT數據盜用和竊取的風險,JWT建議使用加密的HTTPS協議進行傳輸。
^什么是 JWT,為什么要使用 JWT^
(https://www.youtube.com/watch?v=7Q17ubqLfaM&t=1s)
第 1 步:身份驗證/授權
JWT 代表 JSON 網絡令牌。需要注意的是,JWT 用于授權而不是身份驗證。所以你可能會問有什么區別???身份驗證是當您第一次訪問 Web 服務器并使用用戶名和密碼登錄時。通過提供正確的用戶名和密碼,您將作為該用戶進行身份驗證。
一旦您通過身份驗證,用戶就會被授予一個秘密。此密鑰用于 Web 服務器的每個后續操作。因此,在下一個請求中,您會將您的密鑰發送到 Web 服務器,它會驗證該密鑰是否有效,以及它是否有權執行請求的操作,即授權。然后讓我們繼續,JWT/OIDC Auth Method(https://www.vaultproject.io/docs/auth/jwt)詳細描述了該認證。
第 2 步:生成 JWT
身份驗證成功后,將為用戶生成一個 JWT 令牌并由服務器簽名。此 JWT 被發送給用戶以存儲在其瀏覽器的緩存中。按照目前所描述的,您可能會認為這與當前使用的典型流程(即會話 ID)沒有什么不同。JWT 和會話 ID 實現之間的最大區別是 JWT 不需要服務器記住/存儲任何內容。使用會話 ID,服務器必須為所有未來的請求記住/存儲該會話。JWT 包含有關用戶的所有必要信息,以便服務器為所有未來請求驗證 JWT 令牌。
第 3 步:使用 JWT 進行授權
JWT是一個base64編碼的blob,包含授權用戶所需的所有信息。如果解碼JWT,它將包含上面截圖中列出的部分,即標header,payload以及signature。header部分定義了類型(type),即JWT,以及使用的加密/解密算法(alg),即HS256。
payload部分包含一個主題(子)鍵值對,該鍵值對通常是用戶ID,iat代表發出,iat是生成令牌時的時間戳,最后,該部分可以包含與應用程序相關的任何數據(名稱),例如用戶名。此部分可以包含用戶名的鍵值對。但是,提到的鍵值對是最常見的。最后,我們有驗證部分,用于確保發送的 JWT 有效。
1. Header: 用于說明簽名的加密算法等,下面類型的json經過base64編碼后得到JWT頭部。
{ "typ": "JWT", "alg": "HS256"}
2. Payload: 標準定義了7個字段,載荷json經過base64編碼后得到JWT的載荷。
{ iss (issuer):簽發人 exp (expiration time):過期時間 sub (subject):主題 aud (audience):受眾 nbf (Not Before):生效時間 iat (Issued At):簽發時間 jti (JWT ID):編號}
示例:
{ "sub": "1", "iss": "http://localhost:8000/user/sign_up", "iat": 1451888119, "exp": 1454516119, "nbf": 1451888119, "jti": "37c107e4609ddbcc9c096ea5ee76c667"}
3.Signature: 將頭部和載荷用'.'號連接,再加上一串密鑰,經過頭部聲明的加密算法加密后得到簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
了解Gitlab + Vault + JWT 授權
HashiCorp Vault GitLab 集成:為什么以及如何?(https://www.youtube.com/watch?v=VmQZwfgp3aA)
下面的步驟將對Gitlab,Vault,JWT CICD的流程進行詳解。
第 0 步:創建 Gitlab 項目/存儲庫
正如我們將在下面的步驟說明中看到的,您需要首先創建一個Gitlab repo/project。Gitlab為repo生成的project id(下圖所示)將用于指定 Vault 在該項目可以訪問哪些密鑰信息。
第 1 步:配置 Vault 和密鑰
這聽起來很直觀,但要訪問密鑰,它們必須首先存在于 Vault 中。因此,用戶必須首先創建一個 Vault 密鑰路徑并將項目的所有必要密鑰放置在那里。接下來,必須創建一個Vault policy來授予對這些密鑰的權限。
正如您在下圖中看到的那樣,該策略允許讀取和列出存儲在以下路徑中的密鑰:secrets/gitlab/project_1
最后,此策略必須附加到指定身份驗證機制 (JWT) 和綁定聲明(稍后會詳細介紹)的 Vault 角色,該聲明可以訪問附加策略中定義的那些密鑰。
步驟 2a:設置 Gitlab pipeline
接下來,我們需要配置一個 Gitlab CI/CD 管道,使用.gitlab-ci.yml配置。首先,這個文件是在項目 repo 中定義的。雖然此配置可能是各式各樣的,但基本內容將如下圖所示。首先在變量部分定義vault address、vault secret path和vault role。當 CI/CD 管道被觸發時,Gitlab 將生成一個 JWT 作為以下環境變量傳遞給管道CI_JOB_JWT。
在接下來的部分中,我們將深入探討此配置的各個細節部分。但是,首先,此配置需求從 Vault 請求 Vault 令牌。接下來,如果請求成功,則返回一個 Vault 令牌,Gitlab 運行程序可以使用它來請求密鑰。
步驟 2b:生成 JWT
正如在上一節中所述,當觸發 CI/CD 管道時,會生成 JWT。讓我們看一下這個 JSON 文檔,看看它是如何組成的。通常,Gitlab JWT 將包括 Gitlab 服務器、執行管道作業的用戶、Gitlab 項目 ID、Vault 密鑰路徑和 Gitlab 作業詳細信息的信息。下圖顯示了一個生成并發送到 Vault 的示例 JWT 有效payload。
第 3 步和第 4 步:向 Vault 進行身份驗證 + Vault 驗證 JWT
在本節中,我們將把第 3 步(向 Vault 驗證)和第 4 步(Vault 驗證 JWT)合并為一個部分。當 Vault 收到來自 Gitlab 的 JWT payload以及對機密的請求時,它需要驗證 JWT。在下面的分步說明中,我們將在 Vault 上啟用 JWT 身份驗證,這需要bounder_issuer以及要提供的 JKWS 端點。
bound_issuer通常被設置為 Gitlab 的 FQDN,以指示 Vault“誰”可以使用此身份驗證方法并設置一個指向JKWS端點的URL,從而來指示 Vault“如何”驗證 JWT。因此,當 Vault 收到 JWT 請求時,它會確保請求者已被批準使用此身份驗證方法,并且可以驗證 JWT 的創建者。
為了驗證 JWT,Vault 會提取bound_issuer/iss字段,通常是來自 JWT payload的 Gitlab 的 FQDN。接下來,Vault 對提取的字段進行查找以獲取該字段的 JWKS 端點bound_issuer。Vault 使用 JKWS 端點驗證簽名的 JWT payload,以確保 Gitlab 是請求的創建者。
例如,案例中 Gitlab 實例的 FQDN:gitlab.hackinglab.local。 正如您將在下面的分步說明中看到的那樣,我設置bound_issuer到gitlab.hackinglab.local。并且 JWKS 端點設置為jwks_url = "https://gitlab.hackinglab.local/-/jwks"。
那么,這意味著什么?這意味著當 Vault 收到請求訪問密鑰的 JWT 時,將提取iss應該匹配的字段bound_issuer提供給Vault。接下來,Vault 將通過將 JWT 簽名與位于所提供的 JWKS 端點的簽名進行比較來驗證 JWT 簽名。
第 5 步:Vault檢測邊界聲明和附加策略
驗證 JWT payload后,Vault 將提取試圖被假定的 Vault 角色。下面的 Vault 角色聲明它可以用于 JWT 身份驗證,它具有gitlab-vault-readonly附加政策,以及bound_claims定義“誰”的附加參數的部分可以承擔這個角色。
bound_claims部分指出必須滿足以下標準:只有 Gitlab project/repo project_id為2可以擔任此角色,并且僅構建一個 CI/CD 管道master主可以承擔這個角色。
如果JWT有效負載中包含的bound_claims符合此標準,則可以擔任此角色。最后,假設JWT包含此角色的必要條件,Vault將生成一個令牌。此Vault令牌將被授予擔任此角色的能力。
第 6 步和第 7 步:Vault 返回令牌 + Gitlab runner從 Vault 讀取密鑰
在本節中,我將把第 6 步(Vault 返回令牌)和第 7 步(Gitlab runner從 Vault 讀取密鑰)合并為一個部分。在上一節中,生成了一個 Vault 令牌并將其返回給 Gitlab runner。
Gitlab runner可以使用此 Vault 令牌從 Vault 請求密鑰。如上所述,Vault 令牌被授予具有一組已定義策略的角色。當 Vault 收到訪問密鑰的請求時,它將檢查附加到角色的策略,以確保它已被授予訪問此密鑰的權限。
根據下圖,附加到此角色的策略授予對以下路徑的機密的 READ 和 LIST 權限:secrets/gitlab/project_1。那么這是什么意思?假設 Gitlab runner正在嘗試讀取以下密鑰:secrets/gitlab/project_1/password,根據策略,它可以讀取該密鑰。
但是,假設 Gitlab runner正在嘗試讀取以下密鑰:secrets/mysql_db/super_secret_db_passwrd,根據策略,它沒有讀取該密鑰的權限。
前提條件
- 為每個服務生成 DNS A 記錄
- 現有的 Gitlab 基礎環境
- 至少有一個 Gitlab runner
- 預先部署好的 Vault服務
創建一個 Gitlab 存儲庫
正如我在上面的“假設”部分所述,這篇博文假設你有一個 Gitlab EE 實例正在運行
- 登錄到 Gitlab
- 選擇左上角的“項目”
- 選擇“創建空白項目”
進入Vault Gitlab作為項目名稱
選擇“創建項目”
- 選擇左上角的“項目概覽”
- 記錄項目ID
設置Vault
啟用 JWT 身份驗證
- 使用 CLI 工具登錄 Vault
- curl -s -k -X GET https://gitlab.: 8443 ///jwks
測試 Gitlab JWT 身份驗證
- 以管理員身份通過 CLI 登錄 Vault
- echo | openssl s_client -connect gitlab.: 2 >& 1 | sed - ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > /tmp/gitlab.crt
如果 Gitlab 由自簽名證書提供服務,請下拉證書
- vault auth enable jwt
- vault write auth/jwt/config jwks_url= "https://gitlab.:/-/jwks" bound_issuer= "gitlab." jwks_ca_pem= "$( cat /tmp/gitlab.crt) "
- Vault read auth/jwt/config
測試 Vault JWT 身份驗證
創建密鑰
- vault secrets enable -version=2 -path=secrets kv啟用Vault服務
- vault kv put secrets/<secret path> <secret_key>=<secret_value>
- vault kv get secrets/gitlab/project_1取回密碼
設置 Vault 以提供 Gitlab 對密鑰的訪問權限
- Vault Policy
cd GitlabVaultCICD進入相關目錄
cp conf/vault/policies/gitlab-vault-policy.hcl.example conf/vault/policies/gitlab-vault-policy.hcl
vim conf/vault/policies/gitlab-vault-policy.hcl并設置:
- {{ vault_secret_path }}將此設置為密鑰所在的 Vault 路徑
- vault policy write conf/vault/policies/gitlab-vault-policy.hcl
- 示例策略名稱:gitlab-vault-readonly
- Vault role
- 登錄到 Gitlab
- 瀏覽到您要授予對 Vault 機密訪問權限的 Gitlab 存儲庫
- 選擇左上角的“項目概覽”
- 獲取項目 ID
- cp conf/vault/roles/gitlab-jwt-role.json.example conf/vault/roles/gitlab-jwt-role.json
- vim conf/vault/roles/gitlab-jwt-role.json并設置:
a.{{ gitlab_project_id }}– 將此設置為項目 ID
- 上面的圖顯示了project_id 為 2
b.{{ gitlab_vault_role }}
- 上一節中 Vault 策略的名稱 – 使用gitlab-vault-readonly
- cat conf/vault/roles/gitlab-jwt-role.json | vault write auth/jwt/role/gitlab-vault-readonly -
.gitlab-ci.yml - 獲取 Vault 密鑰
- 登錄到 Gitlab
- 瀏覽到您要授予對 Vault 機密訪問權限的 Gitlab 存儲庫
- 選擇左上角的“項目概覽”
- 選擇“新建文件”
- VAULT_ADDR – 設置為 Vault 的 HTTP URL 地址
- {{vault_search_path}}– 設置為上面創建的 Vault 密鑰路徑
- {{gitlab_vault_role}} – 設置為上面為 Gitlab 創建的 Vault 角色
- 保存并退出
- 進入.gitlab-ci.yml作為文件名
- 復制內容conf/gitlab/gitlab-ci-example.yml.example進入.gitlab-ci.yml并設置:
- 選擇左側的 CI/CD > 管道
- 選擇最新的管道作業
- 選擇Vault_secrets stage
總結:
HashiCorp Vault 是一個密鑰管理工具。與 Git 相比,Vault 的核心功能是以本機方式來處理機密信息的。所以對于配置密鑰信息來說,使用 Vault 作為 Config Server 的后端存儲,是一種更好的選擇。
生產環境代碼泄露的危害,一方面是業務邏輯被不懷好意的人分析,容易被找出可利用的漏洞,當然更危險的是如果代碼里面有一些明文存儲secrets、Token、Api Key等,這些內容被別人拿到,服務就很容易遭到致命的攻擊,給我們帶來不可估量的損失 。所以合理存儲程序中的secrets是十分有必要的。
本文也主要介紹了如何使用Vault對基于Gitlab的CICD流程的加密。另外Vault的場景也是非常廣泛的。本文的目的也僅僅是拋磚引玉,通過本文,你可以了解如下的知識點。
- 了解 JWT 授權的工作原理
- 了解授權和認證的區別
- 如何從 Vault 為 Gitlab CI/CD 管道請求密鑰
- 如何設置 Vault JWT 身份驗證方法
- 如何創建一個 .gitlab-ci.yml配置
參考:
在 docker-compose 替換 NGINX 配置中的環境變量(https://stackoverflow.com/questions/56649582/substitute-environment-variables-in-nginx-config-from-docker-compose)
在 GNU/Linux 上手動安裝 GitLab Runner(https://docs.gitlab.com/runner/install/linux-manually.html)
在 Windows 10 上安裝 Hyper-V(https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v)
使用 openssl 從服務器獲取證書(http://using%20openssl%20to%20get%20the%20certificate%20from%20a%20server/)
Vaults – JWT/OIDC 身份驗證方法(https://www.vaultproject.io/docs/auth/jwt)
Youtube – HashiCorp Vault GitLab 集成:為什么以及如何?(https://www.youtube.com/watch?v=VmQZwfgp3aA)
Youtube – 什么是 JWT,為什么要使用 JWT(https://www.youtube.com/watch?v=7Q17ubqLfaM&t=2s)
原文:https://holdmybeersecurity.com/2021/03/04/gitlab-ci-cd-pipeline-with-vault-secrets/