【How-to Guides】Azure Security — Azure KeyVault 金鑰管理

Key Management in the Cloud: Mastering Azure Key Vault Security

Kellen
20 min readMar 31, 2024

1. 什麼是 Azure Key Vault?

Azure Key Vault 是Azure 的託管服務,可用於安全地儲存和存取敏感資訊,如 connection string、密碼、憑證等。從安全角度來看,將應用程式機密儲存在 Azure Key Vault 中始終是一個不錯的解決方案。本篇將介紹如何在 Azure 應用程式服務中使用 Azure Key Vault 的來取得連接字串和秘密(Secrets),並以 Python 實作接取 Key Vault 資訊將機敏資訊注入到應用程式中,而無需將 hard code 在程式碼中。

2. Azure Key Vault 的存取原則說明

若要存取 Key Vault 金鑰保存庫,所有呼叫者(使用者或應用程式)皆必須具有適當的驗證和授權。驗證會建立呼叫者的身分識別。授權決定呼叫者可執行的作業。Azure Key Vault 提供了兩種不同的存取機制,分為「Key Vault 保存庫存取原則」「 Azure 角色型存取控制(RBAC)」

這兩種存取控制方法都可以保護 Azure Key Vault 中的敏感資料,只是在管理和授權存取時會有些不同,需了解其差異以及它們對程式的影響

Option1. 保存庫存取原則

  • 特點:保存庫存取原則是直接設定在 Azure Key Vault 上的,可以精確地指定對特定金鑰、秘密或證書的操作權限。
  • 設定方式:透過 Azure 管理控制台或 Azure Cli 設定,你可以指定特定使用者或應用程式的存取權限。
  • 對碼農的程式影響:當你使用程式從 Key Vault 中讀取秘密或金鑰時,因 Python 程式需要提供驗證憑證,以便於 Key Vault 進行身份驗證。這樣的存取權限是✨㊟直接授予給 Python 應用程式本身,而不是使用者因此,對於 Python 程式來說,影響主要在於運載執行程式背後的載體本身所需的權限,而不是使用者的權限。有點複雜,碼農可以直接看下面的範例程式比較快。

Option2. Azure 角色型存取控制(RBAC)

  • 特點:RBAC 是一種基於角色的存取控制模型,允許你根據角色來授予使用者或應用程式的權限。你可以將使用者分配到預定義的角色,如讀取者、寫入者、擁有者等,也可以自定義角色以滿足特定需求。
  • 設定方式:RBAC 的設定可以在 Microsoft Entra ID(舊稱 Azure Active Directory, AAD)中完成,你可以直接將使用者或應用程式分配到特定的角色
  • 對碼農的程式影響:對於程式來說,影響主要在於所使用的認證方式。如果你使用的是 Azure Managed Identity,則可以直接在 Azure VM 或 Azure Functions 中啟用 Managed Identity 並分配相應的 RBAC 角色。如果你使用的是服務主體(Service Principal)來進行身份驗證,則需將該服務主體分配到具有適當權限的 RBAC 角色。

一開始看官方文件還是有點霧煞煞,沒關係,動手實作看看兩者差異!及對 Python 碼農及管理員怎麼去設計權限控管的機制。

3. 建立 Azure Key Vault

前置作業

# Azure Cli 工具安裝
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

# 安裝依賴項
pip install azure-identity azure-keyvault-secrets
# 查看您在 Linux 上登入的使用者身份 or whoami 也可行
id
# Result
uid=9527(fubar) gid=9527(fubar) groups=9527(fubar)
# --------------------------------------------------------------------
# 開始登入及查看使用者資訊
az login --use-device-code
az account show
# Result
{
"environmentName": "AzureCloud",
"homeTenantId": "x3xoooxx-xxxx-aaaa-9999-xxxxxxxxxxxx",
"id": "cccccccc-xxxx-aaaa-9999-xxxxxxxxxxxx",
"isDefault": true,
"managedByTenants": [],
"name": "HelloAzureWorld",
"state": "Enabled",
"tenantId": "x3xoooxx-xxxx-aaaa-9999-xxxxxxxxxxxx",
"user": {
"cloudShellID": true,
"name": "live.com#fubar@fubar.com",
"type": "user"
}
}

建立一個 Azure Key Vault

目前我把相關的應用程式都集中在 Resource Group,因此我先於 Resource Group: EchoBot-MultiTenant 建立起專用的 Key Vault

至 Marketplace 找到有 🔑就開始進行產置

✨㊟✨軟刪除保護將自動在此金鑰保管庫上啟用。軟刪除是一種保護機制,當您刪除金鑰保管庫或其中的秘密時,它不會立即從系統中永久刪除,而是將其標記為已刪除並放入回收站

Purge protection
清除保護。這是一個進一步的保護機制,用於防止在保留期間之前對金鑰保管庫或其中的對象進行永久刪除。

  • Disable purge protection (allow key vault and objects to be purged during retention period)
    禁用清除保護(允許在保留期間對金鑰保管庫和對象進行清除)。測試關係,我先選這個,因為實驗測試關係會有一些測試來來去去,我想要對金鑰保管庫和其中的物件進行永久刪除
  • Enable purge protection (enforce a mandatory retention period for deleted vaults and vault objects)
    啟用清除保護(對已刪除的金鑰保管庫和金鑰保管庫對象強制執行強制保留期)

管理員(有 KeyVault 管理者權限)建立 demo 用 Secret 供後續在應用程式中引用,稱 bot-connection-string

建完後進行查看,下一層還會有版本資訊

再下層會有秘密識別碼資訊,組成邏輯為
https://{vault-name}.vault.azure.net/secrets/secret-name}/{version}

補充 — Azure Cli 建置方式

如 2 所提到的 KeyVault 分成兩個存取原則,以下就 Option1&2 進行實作

4–1. 設定 KeyVault 保存庫存取原則(Option1.)

在 Azure 入口網站中,您可以透過下列步驟設定 Azure Key Vault 存取原則流程如下:

1️⃣ 登入 Azure 入口網站並導覽至您的 Azure Key Vault
2️⃣ 左側導覽列選擇「存取設定」並在右側選「保管庫存取原則」+「前往」
3️⃣ 點擊「新增存取策略」,然後選擇所需的權限(如取得、列出等)以及要授予存取權限的使用者、服務主體或 Azure 資源

2️⃣ 操作畫面
設定權限 ➤ 主體 ➤ 應用程式 ➤ 建立

已設定好存取原則的使用者及應用程式列表

📝 授予對象(VM, WebApp, Admin…等)可以存取儲存在 KeyVault 的 secret(4–1)
📝 使用 VM 的身分識別取得存取令牌,並使用它從 KeyVault 以取得已儲存的 secret(5–1)

5–1. 在應用程式中使用 Key Vault(Python)

一旦設定了 Azure Key Vault Access Policy,就可以在 Azure 應用程式服務使用 Azure Key Vault 中儲存的 connection string or secrets。

(1) 使用者至 VM 執行以下程式

  • 建立一台 VM 後並安裝以下
sudo apt install python3-pip
# 安裝依賴項
pip install azure-identity azure-keyvault-secrets
  • 將 VM 的 APP ID 加入存取原則
  • 執行 test-auth.py 查看相關存取控制資訊
import os
import jwt
import json
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
from azure.keyvault.secrets import SecretClient

# 設定 Key Vault 的名稱和 URL
os.environ["KEY_VAULT_NAME"] = "your-vault-name"
keyVaultName = os.environ["KEY_VAULT_NAME"]
key_vault_url = f"https://{keyVaultName}.vault.azure.net/"

try:
# 嘗試使用 Managed Identity 取得認證
credential = ManagedIdentityCredential()
print("Managed Identity credential used.")
except Exception as e:
# 如果無法取得 Managed Identity 的認證,則使用預設認證方法
print(f"Error while getting Managed Identity credential: {e}")
credential = DefaultAzureCredential()
print("Default credential method used.")

# 使用 SecretClient 來訪問 Key Vault
client = SecretClient(vault_url=key_vault_url, credential=credential)

# 獲取令牌
token = credential.get_token("https://vault.azure.net")

# 解碼令牌
decoded_token = jwt.decode(token.token, verify=False)

# 印出身份認證的資訊,格式問題不易理解改用下面 json 格式,先廢除!
# print("Authenticated user:", decoded_token)

# 將驗證用戶的資訊以 JSON 格式呈現
json_data = json.dumps(decoded_token, indent=4)
print("Authenticated user:")
print(json_data)

secret_name = "bot-connection-string"
retrieved_secret = client.get_secret(secret_name)

print(f"Your secret is '{retrieved_secret.value}'.")
print(f"Your secret properties not_before(nbf) is '{retrieved_secret.properties.not_before}'.")
print(f"Your secret properties expires_on is '{retrieved_secret.properties.expires_on}'.")

簡易說明這支程式的用途

  • 身份驗證:程式中使用的 ManagedIdentityCredentialDefaultAzureCredential 是 Azure Identity 模組提供的工具,用於幫助 Python 應用程式在 Azure 環境中進行身份驗證
  • 存取 Azure Key Vault:一旦身份驗證成功,程式會使用獲得的身份驗證資訊建立 SecretClient 物件,這個物件可以用於存取 Azure Key Vault 中的機密資訊。

通過這個程式,碼農可以在 Python 中使用 Azure Identity 來驗證身份,然後存取 Azure Key Vault 中的敏感資訊。

🔧 排障點 — 即使我已經把 fubar 這個人加入存取了,但若沒有把運行程式的載體 VM 加入存取權限的狀況會 AccessDenied,因為運作程式的載體是 VM

Code: Forbidden
Message: The user, group or application 'appid=XXXX;oid=OOO;iss=https://sts.windows.net/fubar-teanant-id/'
https://go.microsoft.com/fwlink/?linkid=2125287

Inner error: {
"code": "AccessDenied"
}

(2) Developer 至非 Azure 環境執行程式開發

這個困擾滿久的,特別是在非 Azure 環境開發如 GCP, AWS 開發時,雖已使用 az login 登錄到 Azure 帳戶並且 az account show 顯示了正確的帳戶詳細信息,看起來只代表能使用 Azure CLI 進行操作,但 Python 處理時始終失敗。推測在 Python 可能使用了 Azure SDK(例如 Azure Identity Package)來獲取身份驗證令牌,但應用程式可能缺少適當的許可權來訪問 KeyVault。目前只找到有官方文件提到這樣的作法,但實際原理還需要再釐清。

將 ClientID(a.k.a AppID) + Client Secret + TenantID 加入程式
import os
import cmd
from azure.keyvault.secrets import SecretClient
from azure.identity import DefaultAzureCredential

os.environ["AZURE_CLIENT_ID"] = "xxx"
os.environ["AZURE_CLIENT_SECRET"] = "xxx"
os.environ["AZURE_TENANT_ID"] = "xxx"

keyVaultName = 'your-kv-name'
secretName = 'bot-connection-string'
KVUri = f"https://{keyVaultName}.vault.azure.net"
credential = DefaultAzureCredential()
token = credential.get_token("https://vault.azure.net/.default")
client = SecretClient(vault_url=KVUri, credential=credential)
retrieved_secret = client.get_secret(secretName)

print(f"Your secret is '{retrieved_secret.value}'.")
print(f"Your secret properties not_before(nbf) is '{retrieved_secret.properties.not_before}'.")
print(f"Your secret properties expires_on is '{retrieved_secret.properties.expires_on}'.")

以下使用 Azure Microsoft Entra ID 的功能之一 Managed identity,並使用 Azure RBAC 授權應用程式存取 Azure Key Vault

4–2. 設定 角色型存取控制, RBAC(Option2.)

在您的應用程式服務中,您可以使用 Azure 應用程式服務的 Managed Identity 功能來安全地存取 Azure Key Vault。而 Managed Identity 可以與 Azure 資源(如虛擬機器、函數、Web 應用程式等)整合,讓應用程式獲取 Azure 資源的訪問權限,也可以隨應用程式生命週期而進行刪除。

安全性上來說 Managed Identity 是由 Azure 提供和管理的,因此它與 Azure AD 緊密集成,並符合 Azure 的安全標準和最佳實踐。這使得 Managed Identity 在安全性方面更可靠,不過因為相較 Option1. 使用 KeyVault 存取原則相較複雜,但因為整合性與安全性來說,個人會比較推薦此方式。

管理員建立測試用 Secret 步驟

在建立一個 Azure KeyVault RBACBotDemoSecretRBAC 之後

  • 管理員先實作一個測試用 Secret,並記得要先指派這個管理員有建立 KeyVault 的角色,這樣才有權限建立 secret ~
  • 點選 Key Vault Secrets User 並依應用程序所要運作的選擇合適的 RoleKey Vault Secrets User 這將允許我們提取機密內容但不能修改
若你是管理員先選 KevVault admin

並先了解幾個角色的差異點

secreat user 與 reader 差別是 reader 只能讀取 metadta,無法讀取到其中的值,就看應用程需要什麼權限再配賦最小權限即可!例如:清查所有 secret 有多少個就很適合用 reader
  • 管理員權限新增:點選「使用者、群組或服務主體」

有了權限後即可進行 Demo secret 的建置,若無權限則會報錯!

指派 VM 具有 KeyVault secret User 權限

  • 切換到 VM:啟用 Managed identity,點選左側的 Identity
這實際上是在 Azure AD 中為您的邏輯應用程式建立身分,將允許我們在後續步驟中向邏輯應用程式分配權限
  • 同管理員步驟,來到 KeyVault 選上方的 Add 中的 Add role assignment
  • 因 Python 會取 KeyVault 值,因此這邊 VM 配賦到 SecretUser Role
  • Managed identity 欄位選擇 虛擬機器,欄位選擇所建立的 VM(本範例為 demo-vault-lab)

IAM 畫面如下

5–2. 在應用程式中使用 Key Vault(Python)

直接使用 5–1 程式碼即可運作,並調整一下你的 KeyVault 資源名稱

6. App Service 引用 KeyVault 作法

使用 Key Vault 並應用於 Azure App Service

Azure Web Apps 提供了直接引用 Key Vault 機密作為應用程式設定的功能。這樣做的好處是您可以將機密安全地儲存在 Key Vault 中,而無需在應用程式中更改任何程式碼。

建立系統分配的託管身份

與 4–2, 5–2 步驟一樣,我們首先需要為 Azure Web App 建立託管身分 Managed Identity。這使我們能夠向代表 Web App 的身份授予精細權限。前往 Azure Web App 式並在 Setting 下找到 Identity。

App Service 回到身份識別,因為你一定要給他一個身份識別並再這個 Principle 給他一個 Role,所以KeyVault 才能允許我這個 Role 來存取我裡面的資訊喔!

於 Configuration 引入 KeyVault

  • 點選所建立的 bot-connection-string取得 secret 的 CURRENT VERSIONSecret Identifier 資訊

環境變數這邊設定

  • Name 欄位輸入 BOT_CONNECTION_STRING
  • Value 欄位輸入 @Microsoft.KeyVault(SecretUri=Value_From_Secret_Identifier)
  • Value_From_Secret_Identifier輸入像以下這樣@Microsoft.KeyVault(SecretUri=https://{your-KV-name}.vault.azure.net/secrets/bot-connection-string/{version})

7. 結論

透過使用 Azure Key Vault 這個託管式的工具,可以確保敏感資訊在儲存和存取時得到了充分的保護。同時,將 Azure Key Vault 與 Azure 應用服務、權限整合,可簡化應用程式的開發和部署流程,並提高應用程式的安全性和可維護性。

--

--

Kellen

Backend(Python)/K8s and Container eco-system/Technical&Product Manager/host Developer Experience/早期投入資料創新與 ETL 工作,近期堆疊 Cloud☁️ 解決方案並記錄實作與一些雲端概念💡