Kubernetes 的身份驗證機制對於叢集安全至關重要。本文將探討 OIDC 整合,說明如何利用 id_token 和 refresh_token 進行驗證,並透過 kubectl 設定及 API 互動流程的圖表說明,讓讀者更容易理解其運作方式。同時,文章也分析了憑證驗證和服務帳戶的特性,比較不同驗證機制的優劣,提供讀者在不同情境下選擇合適的驗證策略。最後,文章也強調了 RBAC 的重要性,以及如何結合 OIDC 和 RBAC 進行更精細的許可權管理,確保 Kubernetes 叢集的安全性和穩定性。
Kubernetes 使用者身份識別與 OpenID Connect 整合詳解
Kubernetes 的身份識別機制是其安全架構的核心組成部分,瞭解其運作原理對於企業級安全整合至關重要。本章節將探討 Kubernetes 如何識別使用者身份,並分析 OpenID Connect(OIDC)協定在其中的角色。
Kubernetes 中的使用者身份識別方式
Kubernetes 採用多種方式來識別存取叢集的使用者,主要分為外部使用者和服務帳戶兩大類別。
外部使用者驗證
外部使用者存取 Kubernetes API 主要透過兩種驗證方式:
- 憑證驗證:使用包含使用者資訊(如使用者名稱和群組)的客戶端憑證,作為 TLS 連線協商過程的一部分。
- Bearer Token:在每個請求中嵌入 Bearer Token,該 Token 可以是自包含的,包含所有必要的驗證資訊,或是可透過 API 伺服器的 Webhook 與外部系統交換驗證資訊。
群組管理機制
Kubernetes 中的群組概念允許將相同的許可權分配給多個使用者,而無需為每個使用者個別建立 RoleBinding 物件。系統支援兩種型別的群組:
- 系統分配群組:由 API 伺服器自動分配,以
system:字首開頭,例如system:authenticated代表所有已驗證的使用者。 - 使用者宣告群組:由外部驗證系統在 Token 中提供或透過驗證 Webhook 宣告,無特定的命名規範。
值得注意的是,使用者的唯一識別碼(UID)與群組的主要區別在於 UID 必須是唯一的,而群組名稱則不要求唯一。
服務帳戶機制
服務帳戶是 Kubernetes 中的特殊物件,用於追蹤哪些 Pod 可以存取特定的 API。服務帳戶 Token 採用 JSON Web Tokens(JWT)格式,主要有兩種取得方式:
- 由 Kubernetes 在建立服務帳戶時自動產生的 Secret。
- 透過
TokenRequestAPI,該 API 可用於將 Token 注入 Pod 或在叢集外部使用。
與一般使用者不同,服務帳戶無法被指派到任意群組,而是自動隸屬於預先定義的特定群組。
OpenID Connect(OIDC)整合
OpenID Connect 是根據 OAuth2 的標準身份聯合協定,為 Kubernetes 提供了企業級所需的安全特性。主要優勢包括:
短效期 Token
OIDC 支援產生短效期(1-2分鐘)的 Token,減少 Token 洩漏的風險。
使用者與群組資訊嵌入
Token 中可包含使用者識別碼和群組成員資訊,便於進行精細的存取控制。
Refresh Token 機制
支援 Refresh Token,可根據企業的閒置逾時政策設定有效期限,保持與其他 Web 應用的安全一致性。
原生支援
kubectl 命令列工具原生支援 OIDC,無需額外外掛程式即可使用。
多因素驗證支援
許多強大的多因素驗證機制(如 FIDO U2F 和 WebAuthn)需要瀏覽器支援,OIDC 能夠與這些機制無縫整合。
OIDC 協定運作流程
深入理解 OIDC 協定對於正確組態 Kubernetes 至關重要。主要包含兩個關鍵導向:
- 使用 Token 與
kubectl及 API 伺服器互動。 - 使用 Refresh Token 保持 Token 的有效性。
圖表說明:OIDC 流程示意圖
圖表翻譯: 此圖示展示了 OIDC 認證流程。首先,使用者透過 kubectl 請求存取 Kubernetes 叢集。kubectl 向 OIDC 提供者請求 Token,並獲得 ID Token 和 Refresh Token。接著,kubectl 使用 ID Token 向 Kubernetes API 發起請求。API 會向 OIDC 提供者驗證 Token 的有效性。當 ID Token 即將過期時,kubectl 使用 Refresh Token 向 OIDC 提供者請求新的 Token,以保持連線的有效性。
OpenID Connect 認證在 Kubernetes 中的應用
Kubernetes 支援 OpenID Connect (OIDC) 作為一種驗證機制,讓使用者能夠安全地存取叢集資源。OIDC 是一種根據 OAuth 2.0 的身份驗證協定,提供了一種標準化的方式來驗證使用者身份。
OIDC 登入流程與 Token 型別
當使用者透過 OIDC 登入時,會產生三種 Token:
- access_token:用於向身份提供者 (IdP) 提供的 Web 服務發出驗證請求,例如取得使用者資訊。Kubernetes 不使用此 Token,可忽略。
- id_token:一個 JSON Web Token (JWT),包含使用者的身份資訊,如唯一識別碼 (sub)、群組和過期資訊。Kubernetes 使用此 Token 來驗證使用者身份。
- refresh_token:用於在
id_token過期時自動重新整理。kubectl會使用此 Token 向 IdP 的 Token 端點請求新的id_token。
程式碼範例:設定 kubectl 使用 OIDC Token
kubectl config set-credentials username --auth-provider=oidc \
--auth-provider-arg=idp-issuer-url=https://host/uri \
--auth-provider-arg=client-id=kubernetes \
--auth-provider-arg=refresh-token=$REFRESH_TOKEN \
--auth-provider-arg=id-token=$ID_TOKEN
內容解密:
--auth-provider=oidc指定使用 OIDC 身份驗證提供者。--auth-provider-arg=idp-issuer-url設定 IdP 的簽發者 URL,用於組態 API 伺服器。--auth-provider-arg=client-id指定客戶端 ID,用於識別 Kubernetes 組態。--auth-provider-arg=refresh-token和--auth-provider-arg=id-token分別設定refresh_token和id_token。
OIDC 與 Kubernetes API 的互動流程
以下是 kubectl 組態完成後,API 互動的流程:
@startuml
note
無法自動轉換的 Plantuml 圖表
請手動檢查和調整
@enduml圖表翻譯:
此圖示展示了使用者透過 OIDC 登入並與 Kubernetes API 互動的流程。首先,使用者向 IdP 登入並取得 Token,然後將 Token 組態到 kubectl。接著,kubectl 傳送帶有 id_token 的 API 請求至 API 伺服器,API 伺服器驗證 Token 後進行授權檢查並傳回結果。
重點解析與最佳實踐
- 避免使用 client_secret:由於需要與多個使用者共用,
client_secret可能導致管理上的困擾,因此建議組態 IdP 使用公開端點。 - IdP 支援 Discovery URL:Kubernetes 需要 IdP 支援 Discovery URL (
/.well-known/openid-configuration),以取得 JWT 簽名驗證所需的金鑰資訊。
OpenID Connect 深入解析
Kubernetes 中的 OIDC 認證流程
在 Kubernetes 中使用 OpenID Connect (OIDC) 進行身份驗證是一個複雜的過程,涉及多個步驟和元件。以下是該流程的詳細解析:
使用者端請求: 當使用者使用
kubectl命令列工具與 Kubernetes API 伺服器互動時,kubectl會檢查是否需要進行身份驗證。身份驗證: 如果需要身份驗證,
kubectl會使用 OIDC 協定向 Identity Provider (IdP) 傳送請求,以取得id_token。Token 取得: IdP 驗證使用者的身份後,會發放一個
id_token和一個refresh_token。id_token是一個 JSON Web Token (JWT),包含了使用者的身份資訊和其他宣告(claims)。請求 API:
kubectl使用id_token向 Kubernetes API 伺服器傳送請求。API 伺服器驗證id_token的有效性,包括檢查簽名、發行者(iss)、受眾(aud)和過期時間(exp)等。授權: 一旦
id_token被驗證有效,API 伺服器會根據其 RBAC 組態對使用者的請求進行授權。回應: 如果使用者被授權,API 伺服器會傳回相應的回應給
kubectl。
id_token 詳解
id_token 是一個 base64 編碼的 JWT,包含了一系列的宣告(claims)。重要的宣告包括:
iss: 發行者,必須與kubectl組態中的發行者匹配。aud: 受眾,通常是客戶端 ID。sub: 使用者的唯一識別符號。groups: 使用者所屬的群組,這不是一個標準宣告,但對於 Kubernetes 佈署很重要。exp:id_token的過期時間。iat:id_token的發行時間。nbf:id_token的最早有效時間。
為何需要多個時間相關宣告?
由於不同系統之間的時鐘可能存在偏差,僅有過期時間可能會導致問題。加入 nbf 宣告可以提供一定的時間容錯空間,以適應時鐘偏差。
OIDC Token 的應用場景
除了與 Kubernetes API 伺服器互動外,id_token 還可以被用於其他場景,例如 webhook 呼叫。在使用 OPA(Open Policy Agent)作為驗證 webhook 時,webhook 可以接收到使用者的 id_token,並根據其中的資訊做出相應的策略決定。
示例場景:根據提交者組織對映 PVC 到特定 PV
假設您希望根據提交者的組織將 Persistent Volume Claims (PVCs) 對映到特定的 Persistent Volumes (PVs)。透過將包含組織資訊的 id_token 傳遞給 OPA webhook,您可以在 OPA 策略中使用這些資訊來做出決定。
最佳實踐
確保 IdP 支援會話復原:在選擇 IdP 時,請確保它支援會話復原,以便在必要時能夠立即鎖定使用者。
使用短生命週期的
id_token:為了減少id_token被濫用的風險,應使用短生命週期的id_token(例如1-2分鐘)。正確組態 RBAC:根據使用者的角色和需求正確組態 RBAC,以確保授權正確實施。
整合驗證至叢集的其他驗證選項
在前面的章節中,我們探討了OIDC(OpenID Connect)並說明瞭它為何成為最適合的驗證機制。當然,OIDC並非唯一的選擇,本章節將討論其他可用的驗證選項及其適用的情境。
憑證驗證
憑證驗證通常是每個人首次與Kubernetes叢集互動時採用的驗證方式。Kubernetes安裝完成後,會自動產生一個預設的kubectl組態檔案,其中包含了憑證和私鑰。這個檔案通常只在緊急情況下使用,例如其他所有驗證方式都失效時。它的使用應受到組織對於特權存取的嚴格控制。雖然憑證驗證在某些特定情況下非常有用,但它也有一些限制。
憑證驗證的工作原理
憑證驗證涉及使用客戶端金鑰和憑證來與API伺服器建立HTTPS連線。API伺服器可以取得用於建立連線的憑證,並根據憑證授權單位(CA)的憑證進行驗證。驗證成功後,API伺服器會將憑證中的屬性對映到可識別的使用者和群組。
憑證驗證的安全優勢
要充分發揮憑證驗證的安全優勢,私鑰需要在隔離的硬體上生成,通常以智慧卡的形式存在,並且永遠不會離開該硬體。然後,在該硬體上生成憑證簽署請求,並提交給CA簽署公鑰,從而建立出安裝在專用硬體上的憑證。這種方式下,即使CA被攻破,也無法獲得使用者的私鑰。
為何不應在Kubernetes中使用憑證驗證
雖然憑證驗證看起來是一種很有吸引力的選項,但仍有幾個原因使其並不適合用於Kubernetes:
- 智慧卡整合使用了一種名為PKCS11的標準,而
kubectl和API伺服器並不支援該標準。 - API伺服器無法檢查憑證復原列表或使用OCSP協定,因此一旦憑證被頒發,就無法復原,使得API伺服器仍可使用該憑證。
此外,正確生成金鑰對的過程很少被使用,因為它需要建立一個複雜的介面,且難以與命令列工具結合使用。為了繞過這些問題,通常會為使用者生成憑證和金鑰對,然後下載或透過電子郵件傳送,這就削弱了該過程的安全性。
另一個原因是不易利用群組資訊。雖然可以將群組嵌入到憑證的主題中,但無法復原憑證。因此,如果使用者的角色發生變化,可以給予新的憑證,但無法阻止他們繼續使用舊的憑證。
適當使用場景
正如本文介紹中所述,在「緊急情況」下使用憑證進行身份驗證是合適的。這可能是存取叢集的唯一方式,如果所有其他身份驗證方法都出現問題。
服務帳戶
服務帳戶似乎提供了一種簡單的存取方式。建立它們非常容易,以下命令建立了一個服務帳戶物件和一個與之相關聯的金鑰,用於儲存服務帳戶的令牌:
kubectl create sa mysa -n default
接下來,以下命令將檢索服務帳戶的令牌(JSON格式)並傳回令牌的值。該令牌可用於存取API伺服器:
kubectl get secret $(kubectl get sa mysa -n default -o json | jq -r '.secrets[0].name') -o json | jq -r '.data.token' | base64 -d
使用服務帳戶存取API伺服器
讓我們來示範如何直接呼叫API端點,而不提供任何憑據:
curl -v --insecure https://0.0.0.0:32768/api
你將收到以下回應:
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "forbidden: User \"system:anonymous\" cannot get path \"/api\"",
"reason": "Forbidden",
"details": {},
"code": 403
}
預設情況下,大多數Kubernetes發行版不允許匿名存取API伺服器,因此由於未指定使用者而收到403錯誤。
現在,讓我們將服務帳戶新增到API請求中:
export KUBE_AZ=$(kubectl get secret $(kubectl get sa mysa -n default -o json | jq -r '.secrets[0].name') -o json | jq -r '.data.token' | base64 -d)
curl -H "Authorization: Bearer $KUBE_AZ" --insecure https://0.0.0.0:32768/api
你將收到以下回應:
{
"kind": "APIVersions",
"versions": [
"v1"
]
}
內容解密:
- 建立服務帳戶:首先,使用
kubectl create sa命令建立一個新的服務帳戶。這將自動生成一個與之相關聯的金鑰,用於儲存服務帳戶的令牌。 - 取得服務帳戶令牌:使用
kubectl get secret命令檢索與服務帳戶相關聯的金鑰,並從中提取令牌值。 - 使用令牌存取API伺服器:將提取的令牌新增到HTTP請求頭中,使用
Authorization: Bearer格式,以便透過身份驗證並存取API伺服器。 - API回應:當成功透過身份驗證後,API伺服器將傳回相應的資源或資訊。在這個例子中,回應是可用的API版本列表。