在雲原生應用程式開發中,安全地管理和存取敏感資訊至關重要。Azure Key Vault 提供了安全的機密儲存方案,而 Azure Kubernetes Service (AKS) 則簡化了容器化應用程式的佈署和管理。本文將引導讀者整合 AKS 和 Azure Key Vault,利用 Workload Identity 機制,確保應用程式能以最小許可權原則安全地存取儲存於 Key Vault 中的機密。此方法避免了直接在應用程式碼或設定檔中嵌入敏感資訊,有效提升安全性。首先,我們會建立一個 AKS 叢集,並啟用 OpenID Connect (OIDC) 和 Workload Identity 功能。接著,建立 Azure Key Vault 並設定 RBAC 許可權,確保只有授權的 Kubernetes 服務帳戶才能存取特定機密。然後,我們將使用 Terraform 建立聯盟身份憑證,建立 AKS 與 Azure AD 之間的信任關係。最後,我們會在 AKS 中佈署一個 Pod,並組態 CSI 驅動程式,使其能透過掛載的 Volume 存取 Key Vault 中的機密,同時示範如何設定 Azure Monitor 進行存取稽核,確保所有操作都有紀錄可循。
探索 Azure 雲端密碼儲存系統
在這一章節中,玄貓將帶領讀者利用 Azure CSI Key Vault 外掛,並且 Azure 安全地整合。這次的整合將涉及在 Azure 上使用 Azure Kubernetes Service (AKS) 來建立 Kubernetes 叢集。為了將這兩個元件整合起來,Azure 提供了 Workload Identity 的概念,以便從叢集中獲得細粒度的許可權來存取 Azure Key Vault。
Workload Identity 簡介
Workload Identity 允許我們在 Azure 上的 AKS 中為 Kubernetes 工作負載指派許可權,使其能夠與 Azure 資源互動。例如,我們可能有一個用來存放敏感資訊的 Azure Key Vault。為了與 Azure Key Vault 互動,我們需要一些形式的憑證。Workload Identities 是代表需要身份驗證以與 Azure 資源互動的軟體工作負載的機器身份。與其建立身份或服務主體,我們可以透過手動附加它們的憑證到服務來使用 Workload Identity。這樣,每個服務都可以擁有自己的身份並自行進行身份驗證。
在 Kubernetes 中,我們可以為 Pod 指派 Workload Identity。透過為此身份授予 RBAC 許可權,我們將能夠與 Azure Key Vault 互動。
以下是 Workload Identity 的工作示例:
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Azure Kubernetes Service 整合 Key Vault 安全存取機密
package "Kubernetes Cluster" {
package "Control Plane" {
component [API Server] as api
component [Controller Manager] as cm
component [Scheduler] as sched
database [etcd] as etcd
}
package "Worker Nodes" {
component [Kubelet] as kubelet
component [Kube-proxy] as proxy
package "Pods" {
component [Container 1] as c1
component [Container 2] as c2
}
}
}
api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2
note right of api
核心 API 入口
所有操作經由此處
end note
@enduml此圖示展示了 Pod 如何透過 Kubernetes Service Account 向 Azure Active Directory (AAD) 請求令牌,然後使用該令牌與 Azure Key Vault 互動。
整合 AKS 叢集和 Azure Key Vault
要將 Kubernetes 與 Azure Key Vault 整合,我們需要先建立一個叢集。有多種選擇可以用來建立叢集,每種選擇都適用於特定情況。這裡我們將建立一個簡單的 AKS 叢集;主節點將公開可見,但節點將位於虛擬網路的私有子網中。
組態 Terraform 專案
在建立 Terraform 專案時,我們需要組態狀態。狀態可以儲存在儲存帳戶中:
terraform {
backend "azurerm" {
resource_group_name = "resource-group"
storage_account_name = "storage-account"
container_name = "tfstate"
key = "aks.tfstate"
}
}
組態好 Terraform 後,我們就可以開始在 Azure 上佈署資源了。這裡我們需要在一個資源群組下佈署本章所需的所有資源:
resource "azurerm_resource_group" "ksm_resource_group" {
name = "ksm-resource-group"
}
使用資源群組可以邏輯地將我們的資源與其他資源分開,特別是針對我們想要實作的解決方案。
此外,我們還需要建立一個儲存帳戶來持久化服務日誌:
resource "azurerm_storage_account" "ksm_storage_account" {
name = "ksmlogs"
resource_group_name = azurerm_resource_group.ksm_resource_group.name
...
}
如「Azure Key Vault 概述」部分所述,透過診斷設定,我們可以啟用某個 Azure 資源的日誌流向儲存帳戶。我們剛剛佈署的儲存帳戶將用於此目的。
網路佈署
接下來我們建立一個虛擬網路並分配一部分私有 IP。然後我們會建立一個子網,以便在上面託管 Kubernetes 叢集節點:
resource "azurerm_virtual_network" "ksm_virtual_network" {
name = "ksm-virtual-network"
address_space = ["10.1.0.0/16"]
}
resource "azurerm_subnet" "ksm_subnet" {
name = "ksm-private-subnt"
address_prefixes = ["10.1.0.0/24"]
enforce_private_link_endpoint_network_policies = true
}
enforce_private_link_endpoint_network_policies 被啟用。透過此選項,此子網中的應用程式可以透過內部網路存取 Azure 元件。
AKS 叢集佈署
接下來我們建立一個 AKS 叢集,建立主節點並新增預設節點池:
resource "azurerm_kubernetes_cluster" "ksm_aks" {
name = "ksm-aks"
dns_prefix = "private-aks-cluster"
private_cluster_enabled = false
oidc_issuer_enabled = true
workload_identity_enabled = true
role_based_access_control_enabled = true
default_node_pool {
name = "default"
node_count = 1
vm_size = "Standard_A2_v2"
vnet_subnet_id = azurerm_subnet.ksm_subnet.id
}
}
這裡需要注意的是啟用了 OpenID Connect (OIDC) 功能和 Workload Identity。這使我們能夠為 Kubernetes 工作負載指派角色,從而使其能夠與 Azure Key Vault 介面。
執行 terraform apply 命令後,叢集將被佈署:
$ terraform init
...
$ terraform apply
如果不想透過 Terraform 建立叢集,也可以使用命令列:
$ az aks create -n ksm-aks -g ksm-resource-group --enable-addons azure-keyvault-secrets-provider --enable-oidc-issuer --enable-workload-identity
完成後,我們就可以成功登入叢集了:
$ az aks get-credentials --name ksm-aks --resource-group ksm-resource-group --subscription $subscription --admin
執行上述命令後,我們將設定 kubectl 命令的組態。該組態位於執行 kubectl 命令的工作站上的本地 ~/.kube/config 路徑中。現在應該能夠向叢集發出命令。
建立 Key Vault
接下來我們會建立一個 Key Vault 資源;然後在該 Key Vault 上建立一個金鑰和一個秘密。為了使透過 RBAC 許可權與 Key Vault 介面變得可行,我們會指派細粒度許可權。
首先建立 Azure Key Vault:
resource "azurerm_key_vault" "ksm_key_vault" {
name = "ksm-key-vault"
sku_name = "standard"
enable_rbac_authorization = true
soft_delete_retention_days = 7
}
建立 Service Account 和 RoleBinding
接著, 需要確保 Kubernetes Service Account 能夠與 Azure Resource 請求 Token, 接著就會順利地對Azure Keyvault 做CRUD操作.
以下是範例:
建立 Service Account 在 Kubernetes 中
apiVersion: v1
kind: ServiceAccount
metadata:
name: azure-keyvault-sa # <--- SERVICE ACCOUNT NAME
---
>
namespace: default # <--- NAMESPACE
---
>
annotations:
azure.workload.identity/client-id: "<CLIENT_ID>" # <--- CLIENT ID
---
>
azure.workload.identity/tenant-id: "<TENANT_ID>" # <--- TENANT ID
---
>
secrets:
- name: azure-keyvault-sa-token # <--- SECRET NAME
---
>
建立 RoleBinding 在 Kubernetes 中
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding # <--- ROLE BINDING OR CLUSTER ROLE BINDING
---
>
metadata:
name: azure-keyvault-sa-rolebinding # <--- ROLE BINDING NAME
---
>
namespace: default # <--- NAMESPACE
---
>
subjects:
- kind: ServiceAccount # <--- SERVICE ACCOUNT TYPE
---
>
name: azure-keyvault-sa # <--- SERVICE ACCOUNT NAME
---
>
namespace: default # <--- NAMESPACE
---
roleRef:
kind: Role # <--- ROLE TYPE
---
>
name: pod-reader # <--- ROLE NAME
---
>
apiGroup: rbac.authorization.k8s.io # <--- API GROUP
---
授權 RoleAssignment 在 Microsoft Entra / Azure AD 中
最後, 需要授權 Service Principal 在 Microsoft Entra / Azure AD, 不然無法對 Azure KeyVault 做CRUD操作.
以下是範例:
az role assignment create --assignee "<CLIENT_ID>" --role "<ROLE_NAME>" --scope "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP_NAME>/providers/Microsoft.KeyVault/vaults/<KEY_VAULT_NAME>"
#### 內容解密:
以上步驟包括:
- Service Account:在 Kubernetes 中為特定工作負載(例如 Pod)建立專屬的服務帳戶。
- RoleBinding:為服務帳戶指派特定的角色繫結(RoleBinding),這使得該服務帳戶能夠具備特定的操作許可權。
- RoleAssignment:在 Microsoft Entra / Azure AD 中授予服務主體(Service Principal)所需的角色分配(Role Assignment),以便能夠對特定資源進行操作。
總結來說, 每個程式碼段落都會涵蓋內容包括:
- 程式邏輯:描述每段程式碼如何運作。
- 設計考量:說明為何選擇此設計。
- 技術原理:探討背後的技術原理。
- 潛在改進點:提出可能的改進方法或替代方案。
透過以上步驟, 您就能夠成功地利用 Workload Identity 操作 Azure KeyVault 與 Kubernetes Cluster!
認識 Azure 中的雲端秘密儲存
在現代雲端應用中,秘密管理是至關重要的。Azure 提供了多種方法來管理和存取秘密,其中 Azure Key Vault 是最常見的選擇之一。本文將探討如何在 Azure 中使用雲端秘密儲存,特別是與 Azure Key Vault 和 AKS(Azure Kubernetes Service)的整合。
設定 Azure Key Vault 並啟用 RBAC
首先,我們需要建立一個 Azure Key Vault 並啟用角色型存取控制(RBAC)。這樣可以精確控制誰可以存取秘密。以下是使用 Terraform 設定 Key Vault 和 RBAC 的範例:
resource "azurerm_key_vault" "ksm_key_vault" {
name = "ksm-key-vault"
location = azurerm_resource_group.ksm_resource_group.location
resource_group_name = azurerm_resource_group.ksm_resource_group.name
sku_name = "Standard"
tenant_id = data.azurerm_client_config.current.tenant_id
enable_rbac_authorization = true
}
接下來,我們需要建立一個使用者指派身份,並將其繫結到 Key Vault 的「Key Vault Reader」角色:
resource "azurerm_user_assigned_identity" "keyvault_reader" {
name = "keyvault-reader"
location = azurerm_resource_group.ksm_resource_group.location
resource_group_name = azurerm_resource_group.ksm_resource_group.name
}
resource "azurerm_role_assignment" "ksm_key_vault_reader" {
scope = azurerm_key_vault.ksm_key_vault.id
role_definition_name = "Key Vault Reader"
principal_id = azurerm_user_assigned_identity.keyvault_reader.principal_id
}
內容解密:
以上程式碼展示瞭如何使用 Terraform 建立一個 Azure Key Vault,並啟用 RBAC 授權。我們還建立了一個使用者指派身份,並將其繫結到「Key Vault Reader」角色,以便能夠讀取秘密。
設定 OIDC 憑證
接下來,我們需要設定聯盟身份憑證(Federated Identity Credential),以便 AKS 叢集中的工作負載能夠安全地存取 Key Vault。以下是使用 Terraform 設定 OIDC 憑證的範例:
resource "azurerm_federated_identity_credential" "cred" {
name = "ksm-reader-identity"
identity = azurerm_user_assigned_identity.keyvault_reader.id
issuer = azurerm_kubernetes_cluster.ksm_aks.oidc_issuer_url
audience = ["api://AzureADTokenExchange"]
subject = "system:serviceaccount:default:service-token-reader"
}
內容解密:
這段程式碼展示瞭如何建立一個聯盟身份憑證,將 AKS 叢集中的服務帳戶與 Azure AD 建立信任關係。這樣可以確保服務帳戶能夠安全地存取 Key Vault 中的秘密。
在 AKS 中讀取秘密
當我們完成上述設定後,可以在 AKS 中讀取 Key Vault 的秘密。首先,確認 CSI(Container Storage Interface)外掛已啟用:
kubectl get pods -n kube-system -l 'app in (secrets-store-csi-driver,secrets-store-provider-azure)'
接下來,我們需要建立一個 SecretProviderClass 資源來組態 CSI 驅動程式:
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: keyvault-secrets
spec:
provider: azure
parameters:
usePodIdentity: "false"
clientID: "<your-client-id>"
keyvaultName: "<your-keyvault-name>"
objects: |
array:
- |
objectName: secret1
objectType: secret
- |
objectName: key1
objectType: key
tenantId: "<your-tenant-id>"
最後,我們建立一個服務帳戶並組態 Pod 來使用 Key Vault 中的秘密:
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
azure.workload.identity/client-id: "<your-client-id>"
labels:
azure.workload.identity/use: "true"
name: service-token-reader
namespace: default
---
kind: Pod
apiVersion: v1
metadata:
name: nginx
spec:
serviceAccountName: service-token-reader
containers:
- name: nginx
image: nginx
volumeMounts:
- name: keyvault-secrets
mountPath: "/mnt/secrets-store"
readOnly: true
volumes:
- name: keyvault-secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "keyvault-secrets"
內容解密:
這段程式碼展示瞭如何在 AKS 中組態和使用 SecretProviderClass 資源來讀取 Key Vault 中的秘密。我們還建立了一個 Pod,並將其組態為使用服務帳戶來存取秘密。
檢查存取稽核
最後,我們需要確保能夠對 Key Vault 的存取進行稽核。以下是使用 Terraform 組態診斷設定的範例:
resource "azurerm_monitor_diagnostic_setting" "ksm_key_vault_logs" {
name = "ksm-key-vault-logs"
target_resource_id = azurerm_key_vault.ksm_key_vault.id
storage_account_id = azurerm_storage_account.ksm_storage_account.id
log {
category = "AuditEvent"
enabled = true
retention_policy {
enabled = false # 建議使用 Azure Storage Lifecycle Management 組態保留策略
}
}
}
內容解密:
這段程式碼展示瞭如何使用 Terraform 組態 Azure Monitor 傳送日誌到儲存體帳戶,以便進行稽核。請注意,診斷設定的保留策略應該透過 Azure Storage Lifecycle Management 組態。