4. Azure RBAC实战
Azure RBAC 的核心思想是"在正确的范围内,把正确的角色授予正确的主体"。上一节我们理解了角色定义、安全主体和作用域三要素,这一节把它们落到三个最典型的数据服务上:存储账户、Azure SQL 数据库和 Key Vault。这三个服务的权限模型各有特点——存储账户区分"管理平面"和"数据平面",SQL 数据库有服务器级和数据库级两层,Key Vault 则把密钥、机密、证书的权限拆得很细。如果只授予"参与者"或"所有者",等于把整个服务的读写删权限全部交出,这恰恰是我们要避免的。
先分清管理平面与数据平面
这是分配最小权限时最容易踩的坑。以存储账户为例,授予"存储账户参与者"(Storage Account Contributor)只允许你管理账户本身——查看密钥、配置网络、修改复制策略,但不能读写 Blob 里的数据。要读写数据,必须授予数据平面角色,如"存储 Blob 数据读取者"(Storage Blob Data Reader)或"存储 Blob 数据参与者"(Storage Blob Data Contributor)。很多初学者发现"我明明是参与者却读不了文件",原因就在这里。
下图展示了三个数据服务中管理平面与数据平面角色的对应关系,以及各自的最小权限落点。
用 Azure CLI 分配角色
假设我们有一个资源组 rg-data-prod,里面有一个存储账户 stdataops01、一个 SQL 数据库 sqldb-orders 和一个 Key Vault kv-app-prod。现在需要给一个应用团队的安全主体(这里用服务主体 sp-app-team 表示)分配最小权限:只读 Blob、只读 SQL 数据、只读机密。
第一步,拿到服务主体的对象 ID。角色分配需要的是 --assignee-object-id,而不是应用 ID:
az ad sp show --id <appId> --query id -o tsv
# 假设返回:11111111-2222-3333-4444-555555555555
第二步,为存储账户分配 Blob 数据读取者。注意作用域精确到容器,而不是整个账户:
az role assignment create \
--assignee-object-id 11111111-2222-3333-4444-555555555555 \
--assignee-principal-type ServicePrincipal \
--role "Storage Blob Data Reader" \
--scope "/subscriptions/<subId>/resourceGroups/rg-data-prod/providers/Microsoft.Storage/storageAccounts/stdataops01/blobServices/default/containers/orders"
第三步,为 SQL 数据库分配"SQL DB 读取者",作用域落在数据库级:
az role assignment create \
--assignee-object-id 11111111-2222-3333-4444-555555555555 \
--assignee-principal-type ServicePrincipal \
--role "Reader" \
--scope "/subscriptions/<subId>/resourceGroups/rg-data-prod/providers/Microsoft.Sql/servers/sqlsrv-prod/databases/sqldb-orders"
az role assignment create \
--assignee-object-id 11111111-2222-3333-4444-555555555555 \
--assignee-principal-type ServicePrincipal \
--role "SQL DB Reader" \
--scope "/subscriptions/<subId>/resourceGroups/rg-data-prod/providers/Microsoft.Sql/servers/sqlsrv-prod/databases/sqldb-orders"
第四步,为 Key Vault 分配"Key Vault 机密读取者"。前提是保管库已启用 RBAC 权限模型(enableRbacAuthorization: true),否则角色分配不会生效:
az role assignment create \
--assignee-object-id 11111111-2222-3333-4444-555555555555 \
--assignee-principal-type ServicePrincipal \
--role "Key Vault Secrets Reader" \
--scope "/subscriptions/<subId>/resourceGroups/rg-data-prod/providers/Microsoft.KeyVault/vaults/kv-app-prod"
用 PowerShell 验证权限
分配完成后必须验证,否则你无法确认权限是否真的生效、是否过大。PowerShell 的 Get-AzRoleAssignment 可以按主体列出所有分配:
Connect-AzAccount
$spId = "11111111-2222-3333-4444-555555555555"
Get-AzRoleAssignment -ObjectId $spId |
Select-Object RoleDefinitionName, Scope |
Format-Table -AutoSize
预期输出应只包含三条记录:Storage Blob Data Reader(容器级)、SQL DB Reader(数据库级)、Key Vault Secrets Reader(保管库级)。如果出现了"参与者"或"所有者",说明权限过大,需要删除后重新分配。
更进一步的验证是"以该主体身份实际访问"。可以用 Test-AzKeyVaultSecret 或直接尝试读取机密,确认能读到;再尝试写入机密,确认被拒绝——这才是最小权限的真正证明。对于存储账户,可以用 Get-AzStorageBlob 读取,用 Set-AzStorageBlobContent 写入,观察后者是否返回 403。
常见误区与排查
第一个误区是只分配管理平面角色就以为能访问数据。第二个误区是作用域给得太大,比如把 Blob 读取者授到订阅级,导致该主体能读所有存储账户的 Blob。第三个误区是 Key Vault 仍在使用访问策略模式,此时 RBAC 角色分配不生效,必须先在保管库上切换到 RBAC 模式。
排查时按这个顺序:先确认主体对象 ID 是否正确(服务主体和应用 ID 容易混淆),再确认作用域路径是否拼写正确,然后确认角色名称是否与内置角色完全一致,最后确认服务的权限模型(Key Vault 的 RBAC/访问策略、存储的平面区分)。这四步能覆盖绝大多数"分配了却没权限"的问题。
az role assignment delete --assignee <objectId> --role <roleName> --scope <scope>。删除前务必确认作用域,避免误删其他团队在同一资源上的授权。把这三个服务的最小权限分配串起来看,规律是一致的:先判断需要的是管理平面还是数据平面权限,再把作用域收窄到实际需要的最小对象(容器、数据库、单个机密),最后用 PowerShell 列出分配并做一次实际访问测试。做到这三点,RBAC 才真正从"能授权"变成"授得准"。