fbpx

HashiCorp VaultㄧASAPP 在 Kubernetes 上使用 Operator 模式實現自動化端到端服務

挑戰

在大約三年前 ASAPP 開始使用 Kubernetes,不久後開始使用 Vault。因為機密管理和 Kubernetes 的設計不是那麼完善,無論是既有的平台或產品上,而 Vault 可以輕鬆達成所需。
當時,Kubernetes 是 1.9 版本,而 Vault 是 0.8 或 0.9 的版本。我們獲得的建議是盡可能單獨運行 Vault。事實上,在 Kubernetes 中運行它的工具幾乎不存在。因此,我們在 EC2 虛擬機上運行它。 

使用產品

HashiCorp 、Kubernetes、Vault

VM 到 Kubernetes 上運行 Vault 的歷程

我們是 AWS shop,擁有網路邊界的 VPC,Kubernetes 叢集在此與 Vault 通訊,生產工作負載會使用 Service Account 身份運行。因為 Vault 發布的 Kubernetes OAuth 後端才剛上市,因此讓我們可以自動使用身份驗證方案。
SRE 團隊可以同時維護 Vault 配置和靜態機密(static secrets)。Vault 配置分為兩部分並用 Terraform 維護它們。第一,本身 Vault 叢集到形成 Vault 叢集的雲端資源以及角色、策略、選項等後端配置。第二,單獨管理靜態機密。

每個服務的配置和抽象化

我們已經足夠抽像出每個服務的配置。因此,對於需要透過 Vault 進行身份驗證的任何新服務,服務所有者只需添加在在此列表中,就會傳遞給一個 Terraform 模組,該模組將建立服務所需的一切,包含策略權限、角色等。

Static Secrets 靜態機密

當時 Vault UI 只有 Enterprise版本,我們使用的是開源的版本,因此靜態機密的 KV 後端還在第一版。
我們的問題是修改附加密鑰或修改協助機密十分複雜。首先,我們必須在命令行中執行此操作,導出至 JSON 文件、修改、重新導入導入,而手動操作十分容易出錯。我們最終採用了使用 KMS 離線加密純文本機密的模式,然後將該 KMS 儲存在 Source Control 中。

字符串(String)不是實際的機密,而是它的 KMS 加密版本。我們把它放在一個 Terraform 中,我們有一個資料源物件,可以動態解密它,然後 vault generic secret 導入 Vault。顯然,這樣做是有風險的,但是透過使用 Terraform in-memory 後端可以減少此風險。

即使它沒有記錄,您可以將此 in-memory 後端與空塊一起使用。後端僅存在in-memory,並且只存在於運行期間,不存在機密以純文本形式持久遠程的狀態。

多個微服務和單租戶部署以建立高基數

現實情況是我們有多個單租戶 Kubernetes 叢集的部署模型。它們每個都有對應的 Vault 叢集而配置幾乎完全相同。
最重要的是,我們正在經歷多個微服務的爆炸式增長。意味著任何修改牽一髮動全身,要對多個微服務、環境的配置和機密進行多次修改,而不是單個微服務和單個環境而已。

無法持續整合/部署(CI/CD)

如果我們有某種形式的 CI/CD 來進行這些更改,這通常不會有問題,但SRE 團隊遇到一個瓶頸,一切都需要手動應用。

Code 是集體所有,但資源不是

服務自助化的觀念導致服務所有者認為他們可以用 Vault 對服務進行身份驗證和配置。現實情況是,服務所有者的介面都停止了。 PR 批准合併和申請必須由 SRE 團隊完成。該團隊擁有 Terraform 後端的權限以及 Vault 和解密 KMS 的權限。它與 SRE 團隊的耦合度太高,無法實現真正的自助服務。

Kubernetes 運行的優勢

Kubernetes Operator Pattern

Kubernetes 本身就像一個循環運行的大控制器,將物件排解或宣告物件為真實的物件,如 pod 和容器等等。但最重要的是,您可以運行客製化控制器層來排解客製化資源定義。這是另一個 Kubernetes 概念,它們是任意資料結構,用於定義事物的屬性和狀態。無論是您的業務領域中的事物、實體物件、外部 API 等。

它不僅限於 Kubernetes API。控制器不需要只作用於 Kubernetes 物件,也可以與外部 API 整合。例如,在我們的案例中,我們與 AWS API 以及 Vault 後端整合。

與我們所擁有的 Terraform 設置相比,主要優勢之一是減少了偏移(drift)。它持續確保狀態按定義運行。Terraform 應用不僅僅是一個好的 Cron 作業,因為它合併了所有 API,以及在每個 cron sec 或每個事件的循環上運行,以確保事情如你定義一樣。

Kubernetes 的另一個方面是導入控制器。它們是攔截對 Kubernetes API 調用的 webhook。無論是您是人為運行 kubectl apply,還是在機器運行(例如 Jenkins 或 Spinnaker)或其他控制器正在與 Kubernetes API 溝通,這些 webhook 將攔截請求。

其中一種是驗證 webhook,它能檢查有效負載,拒絕請求或將其傳遞。另一種變異 webhook 檢查有效負載,可以在將其傳回並在 Kubernetes 儲存、SCD 或任何您擁有的東西中處理先前的修改。
驗證 webhook 的主要用例是強制執行超出 RBAC 的規則或限制,無論用戶或角色是否有權執行操作。對於 mutating webhook,您可以使用運行時已知的東西來清理輸入或修改物件。

Vault in Kubernetes

首先,我們想利用 Kubernetes 本身作為自動化平台。我們的服務已經在使用 Kubernetes 身份驗證,因此引入 Vault 並沒有帶來太多好處。但它減少必須維護額外 EC2 實例集的許多風險或操作複雜性。更重要的是,我們希望將配置和機密管理的所有權交給服務團隊。我們希望移除人工作業以及 SRE 團隊,而不必依賴外部團隊。

下文是我們正在查看的內容以及我們最終得到的內容。首先,現在都在 Kubernetes 中運行,看起來都一樣,我們仍然有工作負載,使用 Service Account 身份運行的 pod 向 Vault 進行身份驗證。

但我們現在有一個 Vault Operator,負責在內部建立 Vault 資源、pod 和服務等。我們有另一個操作人員負責發現需要為 Vault 身份驗證配置的身份,並為此重新配置 Vault。我們有修改工作負載的變異 webhook,以便他們可以發現 Vault,或知道要與哪個 Vault 溝通。

Vault Operator

對於 Vault Operator,我們使用的是開源版本,由 Banzai Cloud 公司維護。它被稱為 Bank-Vaults。這實際上是在您的叢集中安裝了一個名為 Vault 的新的自定義資源,您可以在該物件中定義 Vault 叢集的狀態和配置。
這個單一物件用包含所有內容的單一物件取代了我們用於雲資源和配置的 Terraform。最重要的是,它有一個可以被其他東西修改的程式介面。

Vault Dynamic Configuration Operator

我們的生產工作負載使用 Service Account 物件來使用該身份來識別 Vault,並將 job更換為 Vault 令牌
操作人員需要將配置的 Service Accounts 添加到其中。這裡的關鍵是操作人員不是直接修改 Vault,而是在修改之前定義的 Vault 物件。

然後 Vault Operator 接受這些變更並對後端進行更改。運行時的行為沒有改變,因為從服務的角度來看,它仍然使用其中的一些身份與同一個 Vault 通訊,並使用同一個工作流進行身份驗證。

過去如果想要在程式碼前四行聲明 Service Account,就需要添加一個註解,寫著「我希望自動配置」。現在,Vault 後端將擁有角色策略和配置,以便使用 Service Account 的任何服務都能夠與之對話。不需要任何手動參與,因為一切都是在幕後以程式化方式進行。

Mutating Webhooks

Webhook 的想法是透過刪除他們需要知道或修改的事物的表面區域,來降低服務所有者的複雜性。我們想避免 copy-paste,因為也許它們會從以前整合中複製,但現在已經不兼容或需要複製邊車(sidecar) 定義。
最重要的是,sidecar 本身抽像出我所指的身份驗證服務。這項服務必須了解使用 Kubernetes 特定身份驗證端點的流程,而不是直接請求機密,因為它不需要知道其餘的事情。

因為我們知道邊車是有爭議的。從長遠來看,我們希望它會趨於一致,但我們也希望為想擁有更多掌控權的團隊明確地提供選擇。您可以擁有自己的 Vault 相關配置並進行相同的邊車定義,或者您可以使用註解,聲明您的意圖,然後 webhook 將會接管。

Vault Agent Auto-Inject

在現實生活中是這樣的,例如,我有我的部署定義,我想在那裡導入一個邊車。但我不想定義邊車,我希望 webhook 為我做這件事。

我添加了一個註解,上面寫著「我希望在這個部署定義中導入邊車。」當該 pod 被調度時,webhook 將攔截請求並導入 Vault agent sidecar。邊車定義未在部署清單中明確聲明,實際上,如果您執行 kubectl describe deployment 之類的操作,您將看不到它。但是,如果您執行 kubectl describe pod,您會看到它,因為它是及時導入的,就在 pod 被安排之前。對於為 Vault 進行直接整合的服務不需要透過邊車。他們還可以請求明確定義環境變量。

我需要定義預填寫的變量。在運行時,當 pod 被調度時,變異的 webhook 將導入 Vault address,Vault CA cert 等等。它不僅會掛載值,還會掛載儲存裝置,並且需要 CA 憑證。所有這些值都是 webhook 先前知道的,因為它是預先配置的,這就是我們抽象所有資源的地方。

KMS Vault Operator

我們有 KMS Vault Operator 現在有一個新的物件種類,它被稱為 KMS Vault Secret。它是 Kubernetes 之前定義的自定義資源。
它遵循相同的流程,我們使用 KMS 離線加密機密,我們將其儲存在原始碼控制中。它將成為程式碼及機密,因為現在它是 YAML 中的一個 Kubernetes 物件,你可以將它嵌入到你的 helm charts 或 pipelines 中。Operator 會發現這些物件,然後讀取 KMS 字符串,在 in-memory 解密,導入 Vault。

實際上,它看起來與 Terraform 模式非常相似。您擁有名為 KMS Vault Secret 的新物件類型、將被建立機密的路徑、 KV 版本(例如 V1)、密鑰和加密的機密。同樣,沒有任何純文字的紀錄。因此我們回到建立和配置 Vault、改變工作負載和導入機密的部分。除了剛開始的必要條件和建立之外,這一切都不需要人力。

相關文章